【问题标题】:Does JavaScript have the interface type (such as Java's 'interface')?JavaScript 是否具有接口类型(例如 Java 的“接口”)?
【发布时间】:2011-04-12 05:40:10
【问题描述】:

我正在学习how to make OOP with JavaScript。有没有接口的概念(比如Java的interface)?

所以我可以创建一个监听器...

【问题讨论】:

标签: javascript oop


【解决方案1】:

没有“这个类必须有这些功能”的概念(也就是说,本身没有接口),因为:

  1. JavaScript 继承基于对象,而不是类。在您意识到之前,这没什么大不了的:
  2. JavaScript 是一种非常动态类型的语言——您可以使用适当的方法创建一个对象,这将使其符合接口,然后取消定义所有构成它的东西符合。颠覆类型系统非常容易——即使是偶然的! -- 一开始就尝试制作类型系统是不值得的。

相反,JavaScript 使用所谓的duck typing。 (如果它像鸭子一样走路,像鸭子一样嘎嘎叫,就 JS 而言,它就是一只鸭子。)如果你的对象有 quack()、walk() 和 fly() 方法,代码可以在任何它期望的地方使用它一个可以行走、嘎嘎和飞行的对象,不需要实现一些“Duckable”接口。接口正是代码使用的一组函数(以及这些函数的返回值),并且通过鸭子类型,您可以免费获得。

现在,这并不是说如果您尝试调用 some_dog.quack(),您的代码不会在中途失败;你会得到一个 TypeError。坦率地说,如果你告诉狗嘎嘎,你的问题会稍微大一点。当您将所有鸭子排成一排时,鸭子打字效果最好,可以这么说,并且除非您将它们视为普通动物,否则不要让狗和鸭子混在一起。换句话说,即使界面是流动的,它仍然存在;将狗传递给期望它首先会嘎嘎飞的代码通常是错误的。

但是,如果您确定自己在做正确的事情,则可以通过在尝试使用特定方法之前测试其是否存在来解决嘎嘎狗问题。类似的东西

if (typeof(someObject.quack) == "function")
{
    // This thing can quack
}

因此,您可以在使用之前检查所有可以使用的方法。不过,语法有点难看。还有一种更漂亮的方式:

Object.prototype.can = function(methodName)
{
     return ((typeof this[methodName]) == "function");
};

if (someObject.can("quack"))
{
    someObject.quack();
}

这是标准的 JavaScript,所以它应该可以在任何值得使用的 JS 解释器中工作。它具有像英语一样阅读的额外好处。

对于现代浏览器(即除了 IE 6-8 之外的几乎所有浏览器),甚至还有一种方法可以防止属性出现在 for...in

Object.defineProperty(Object.prototype, 'can', {
    enumerable: false,
    value: function(method) {
        return (typeof this[method] === 'function');
    }
}

问题在于 IE7 对象根本没有.defineProperty,而在 IE8 中,据称它仅适用于宿主对象(即 DOM 元素等)。如果兼容性是一个问题,则不能使用.defineProperty。 (我什至不会提到 IE6,因为它在中国以外已经无关紧要了。)

另一个问题是,一些编码风格喜欢假设每个人都写不好的代码,并禁止修改Object.prototype,以防有人想盲目使用for...in。如果您关心这一点,或者正在使用(IMO broken)代码,请尝试稍微不同的版本:

function can(obj, methodName)
{
     return ((typeof obj[methodName]) == "function");
}

if (can(someObject, "quack"))
{
    someObject.quack();
}

【讨论】:

  • 它并没有想象的那么可怕。 for...in 是 - 并且一直是 - 充满了这样的危险,任何这样做的人至少没有考虑到有人添加到 Object.prototype (该文章自己承认这是一种并不少见的技术)将看到他们的代码被破坏在别人的手中。
  • @entonio:我认为内置类型的延展性是一个特性,而不是一个问题。这是使 shims/polyfills 可行的重要部分。没有它,我们要么用可能不兼容的子类型包装所有内置类型,要么等待通用浏览器支持(这可能永远不会出现,当浏览器不支持某些东西时,因为人们不使用它,因为浏览器不支持'不支持)。因为可以修改内置类型,所以我们可以添加许多尚不存在的函数。
  • 在 Javascript 的最新版本(1.8.5)中,您可以将对象的属性定义为不可可枚举。这样可以避免for...in 问题。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • @Tomás:可悲的是,直到每个浏览器都运行与 ES5 兼容的东西,我们仍然不得不担心这样的事情。即便如此,“for...in 问题”在某种程度上仍然存在,因为总会有草率的代码……嗯,那,Object.defineProperty(obj, 'a', {writable: true, enumerable: false, value: 3});obj.a = 3; 要做的工作要多得多。我完全可以理解人们不经常尝试这样做。 :P
  • 呵呵......喜欢“坦率地说,如果你告诉狗嘎嘎,你的问题会稍微大一点。很好的类比表明语言不应该试图避免愚蠢。这总是一个失败的战斗。-斯科特
【解决方案2】:

获取Dustin Diaz 的“JavaScript design patterns”副本。有几章专门介绍通过 Duck Typing 实现 JavaScript 接口。这也是一本不错的读物。但是不行,接口没有语言原生实现,你必须Duck Type

// example duck typing method
var hasMethods = function(obj /*, method list as strings */){
    var i = 1, methodName;
    while((methodName = arguments[i++])){
        if(typeof obj[methodName] != 'function') {
            return false;
        }
    }
    return true;
}

// in your code
if(hasMethods(obj, 'quak', 'flapWings','waggle')) {
    //  IT'S A DUCK, do your duck thang
}

【讨论】:

  • “pro javascript design patterns”一书中描述的方法可能是我在这里读到的以及我尝试过的一堆东西中最好的方法。您可以在它之上使用继承,这使得遵循 OOP 概念变得更好。有些人可能会声称您在 JS 中不需要 OOP 概念,但我不敢苟同。
【解决方案3】:

JavaScript(ECMAScript 版本 3)有一个 implements 保留字 saved up for future use。我认为这正是为了这个目的,但是,他们急于将规范推出门外,他们没有时间定义如何处理它,所以,目前,浏览器除了让它坐在那里,如果你试图用它做某事,偶尔会抱怨。

创建自己的 Object.implement(Interface) 方法是可能的,而且确实很容易,只要在给定的对象中未实现一组特定的属性/函数,该方法就会停止。

我在object-orientation写了一篇文章where use my own notation as follows

// Create a 'Dog' class that inherits from 'Animal'
// and implements the 'Mammal' interface
var Dog = Object.extend(Animal, {
    constructor: function(name) {
        Dog.superClass.call(this, name);
    },
    bark: function() {
        alert('woof');
    }
}).implement(Mammal);

有很多方法可以给这只特定的猫换皮,但这是我在自己的接口实现中使用的逻辑。我发现我更喜欢这种方法,而且它易于阅读和使用(如上所示)。这确实意味着向Function.prototype 添加一个“实现”方法,有些人可能对此有疑问,但我发现它工作得很好。

Function.prototype.implement = function() {
    // Loop through each interface passed in and then check 
    // that its members are implemented in the context object (this).
    for(var i = 0; i < arguments.length; i++) {
       // .. Check member's logic ..
    }
    // Remember to return the class being tested
    return this;
}

【讨论】:

  • 这个语法真的很伤脑筋,不过这里的实现还是挺有意思的。
  • Javascript 一定会这样做(伤害大脑),特别是来自更干净的 OO 语言实现时。
  • @StevendeSalas:嗯。当您不再试图将 JS 视为面向类的语言时,它实际上会变得非常干净。模拟类、接口等所需的所有废话......这会让你的大脑真正受伤。原型?简单的东西,真的,一旦你停止对抗它们。
  • 什么在“// ..检查成员的逻辑。” ?那是什么样子的?
  • 嗨@We,检查成员逻辑意味着循环遍历所需的属性,如果缺少一个则抛出错误..类似于var interf = arguments[i]; for (prop in interf) { if (this.prototype[prop] === undefined) { throw 'Member [' + prop + '] missing from class definition.'; }}。有关更详细的示例,请参阅article link 的底部。
【解决方案4】:

JavaScript 接口:

虽然 JavaScript interface 类型,但经常需要它。由于与 JavaScript 的动态特性和原型继承的使用有关的原因,很难确保跨类的接口一致——但是,可以这样做;并经常被模仿。

在这一点上,有一些特殊的方法可以在 JavaScript 中模拟接口;方法上的差异通常可以满足一些需求,而另一些则没有得到解决。很多时候,最稳健的方法过于繁琐,并且会妨碍实施者(开发人员)。

这是一种接口/抽象类的方法,它不是很麻烦,是解释性的,将抽象内部的实现保持在最低限度,并为动态或自定义方法留出足够的空间:

function resolvePrecept(interfaceName) {
    var interfaceName = interfaceName;
    return function curry(value) {
        /*      throw new Error(interfaceName + ' requires an implementation for ...');     */
        console.warn('%s requires an implementation for ...', interfaceName);
        return value;
    };
}

var iAbstractClass = function AbstractClass() {
    var defaultTo = resolvePrecept('iAbstractClass');

    this.datum1 = this.datum1 || defaultTo(new Number());
    this.datum2 = this.datum2 || defaultTo(new String());

    this.method1 = this.method1 || defaultTo(new Function('return new Boolean();'));
    this.method2 = this.method2 || defaultTo(new Function('return new Object();'));

};

var ConcreteImplementation = function ConcreteImplementation() {

    this.datum1 = 1;
    this.datum2 = 'str';

    this.method1 = function method1() {
        return true;
    };
    this.method2 = function method2() {
        return {};
    };

    //Applies Interface (Implement iAbstractClass Interface)
    iAbstractClass.apply(this);  // .call / .apply after precept definitions
};

参与者

规则解析器

resolvePrecept 函数是一个实用程序和辅助函数,可在您的抽象类中使用。它的工作是允许对封装的规则(数据和行为)进行自定义的实现处理。它可以抛出错误或警告 -- 并且 -- 为 Implementor 类分配一个默认值。

iAbstractClass

iAbstractClass 定义了要使用的接口。它的方法需要与它的 Implementor 类达成默契。该接口将每个 precept 分配给完全相同的 precept 命名空间 - 或者 - 分配给 Precept Resolver 函数返回的任何内容。但是,默认协议解析为 上下文——实施者的一项规定。

实施者

实施者简单地“同意”一个接口(在本例中为iAbstractClass)并通过使用构造函数劫持来应用它:iAbstractClass.apply(this)。通过定义上面的数据和行为,然后劫持接口的构造函数——将实现者的上下文传递给接口的构造函数——我们可以确保实现者的覆盖将被添加,并且接口将解释警告和默认值价值观。

这是一种非常不繁琐的方法,随着时间的推移和不同的项目,它对我的​​团队和我都非常有用。但是,它确实有一些注意事项和缺点。

缺点

虽然这有助于在很大程度上实现整个软件的一致性,但它并没有实现真正的接口——而是模拟它们。虽然定义、默认值和警告或错误都已说明,但对使用的说明由开发人员强制和断言(与许多 JavaScript 开发一样)。

这似乎是“JavaScript 中的接口” 的最佳方法,但是,我希望看到以下问题得到解决:

  • 返回类型的断言
  • 签名断言
  • delete 操作中冻结对象
  • 断言 JavaScript 社区的特殊性中普遍存在或需要的任何其他内容

也就是说,我希望这对你和我的团队一样有帮助。

【讨论】:

    【解决方案5】:

    希望,任何仍在寻找答案的人都会觉得它有帮助。

    您可以尝试使用代理(自 ECMAScript 2015 起成为标准):https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy

    latLngLiteral = new Proxy({},{
        set: function(obj, prop, val) {
            //only these two properties can be set
            if(['lng','lat'].indexOf(prop) == -1) {
                throw new ReferenceError('Key must be "lat" or "lng"!');
            }
    
            //the dec format only accepts numbers
            if(typeof val !== 'number') {
                throw new TypeError('Value must be numeric');
            }
    
            //latitude is in range between 0 and 90
            if(prop == 'lat'  && !(0 < val && val < 90)) {
                throw new RangeError('Position is out of range!');
            }
            //longitude is in range between 0 and 180
            else if(prop == 'lng' && !(0 < val && val < 180)) {
                throw new RangeError('Position is out of range!');
            }
    
            obj[prop] = val;
    
            return true;
        }
    });
    

    那么你可以很容易地说:

    myMap = {}
    myMap.position = latLngLiteral;
    

    如果您想通过instanceof(@Kamaffeather 询问)进行检查,您可以将其包装在一个对象中,如下所示:

    class LatLngLiteral {
        constructor(props)
        {
            this.proxy = new Proxy(this, {
                set: function(obj, prop, val) {
                    //only these two properties can be set
                    if(['lng','lat'].indexOf(prop) == -1) {
                        throw new ReferenceError('Key must be "lat" or "lng"!');
                    }
    
                    //the dec format only accepts numbers
                    if(typeof val !== 'number') {
                        throw new TypeError('Value must be numeric');
                    }
    
                    //latitude is in range between 0 and 90
                    if(prop == 'lat'  && !(0 < val && val < 90)) {
                        throw new RangeError('Position is out of range!');
                    }
                    //longitude is in range between 0 and 180
                    else if(prop == 'lng' && !(0 < val && val < 180)) {
                        throw new RangeError('Position is out of range!');
                    }
    
                    obj[prop] = val;
    
                    return true;
                }
            })
            return this.proxy
        }
    }
    

    这可以不使用Proxy 而是使用getterssetters 类来完成:

    class LatLngLiteral {
        #latitude;
        #longitude;
    
        get lat()
        {
            return this.#latitude;
        }
    
        get lng()
        {
            return this.#longitude;
        }
        
        set lat(val)
        {
            //the dec format only accepts numbers
            if(typeof val !== 'number') {
                throw new TypeError('Value must be numeric');
            }
    
            //latitude is in range between 0 and 90
            if(!(0 < val && val < 90)) {
                throw new RangeError('Position is out of range!');
            }
            
            this.#latitude = val
        }
        
        set lng(val)
        {
            //the dec format only accepts numbers
            if(typeof val !== 'number') {
                throw new TypeError('Value must be numeric');
            }
    
            //longitude is in range between 0 and 180
            if(!(0 < val && val < 180)) {
                throw new RangeError('Position is out of range!');
            }
            
            this.#longitude = val
        }
    }
    

    【讨论】:

    • 有没有办法使用代理也有一个可以通过instanceof检查的命名接口?喜欢true === myMap.position instanceof latLngLiteral
    • @Kamafeather 好吧,稍微改变一下,这是可能的(见我更新的答案)
    【解决方案6】:

    您需要 Java 中的接口,因为它是静态类型的,并且在编译期间应该知道类之间的约定。在 JavaScript 中是不同的。 JavaScript 是动态类型的;这意味着当您获取对象时,您可以检查它是否具有特定的方法并调用它。

    【讨论】:

    • 实际上,Java 中不需要接口,确保对象具有特定的 API 是一种故障保险,因此您可以将它们换成其他实现。
    • 不,Java 实际上需要它们,以便它可以为在编译时实现接口的类构建 vtables。声明一个类实现了一个接口指示编译器构建一个小结构,其中包含指向该接口所需的所有方法的指针。否则,它必须在运行时按名称调度(就像动态类型语言一样)。
    • 我不认为这是正确的。在 java 中调度总是动态的(除非方法可能是最终的),并且方法属于接口的事实不会改变查找规则。静态类型语言需要接口的原因是您可以使用相同的“伪类型”(接口)来引用不相关的类。
    • @entonio:调度并不像看起来那样动态。由于多态性,实际方法通常直到运行时才知道,但字节码并没有说“invoke yourMethod”;它说“调用 Superclass.yourMethod”。 JVM 无法在不知道要在哪个类中查找方法的情况下调用方法。在链接期间,它可能会将 yourMethod 放在 Superclass 的 vtable 中的条目 #5 处,并且对于每个具有自己的 @987654323 的子类@,只需将子类的条目 #5 指向适当的实现。
    • @entonio:对于接口,规则 do 会有所改变。 (不是语言,但生成的字节码和 JVM 的查找过程是不同的。)一个名为 Implementation 的类实现了 SomeInterface 并不仅仅说它实现了整个接口。它的信息显示“我实现了SomeInterface.yourMethod”并指向Implementation.yourMethod 的方法定义。当 JVM 调用 SomeInterface.yourMethod 时,它会在类中查找有关该接口方法实现的信息,并发现它需要调用 Implementation.yourMethod
    【解决方案7】:

    这样的抽象界面

    const MyInterface = {
      serialize: () => {throw "must implement serialize for MyInterface types"},
      print: () => console.log(this.serialize())
    }
    

    创建一个实例:

    function MyType() {
      this.serialize = () => "serialized "
    }
    MyType.prototype = MyInterface
    

    并使用它

    let x = new MyType()
    x.print()
    

    【讨论】:

      【解决方案8】:

      当你想使用转编译器时,你可以试试 TypeScript。它支持 ECMA 功能草案(在提案中,接口称为“protocols”),类似于 coffeescript 或 babel 等语言所做的。

      在 TypeScript 中,您的界面可能如下所示:

      interface IMyInterface {
          id: number; // TypeScript types are lowercase
          name: string;
          callback: (key: string; value: any; array: string[]) => void;
          type: "test" | "notATest"; // so called "union type"
      }
      

      你不能做什么:

      【讨论】:

        【解决方案9】:

        JavaScript 中没有原生接口, 有几种方法可以模拟界面。我已经写了一个包

        可以看到植入here

        【讨论】:

          【解决方案10】:

          Javascript 没有接口。但它可以是鸭子类型的,可以在这里找到一个示例:

          http://reinsbrain.blogspot.com/2008/10/interface-in-javascript.html

          【讨论】:

          • 我喜欢该链接上的文章用来对类型进行断言的模式。当某些东西没有实现它应该实现的方法时抛出的错误正是我所期望的,如果我这样做的话,我喜欢如何将这些必需的方法组合在一起(如接口)。
          • 我讨厌转译(以及用于调试的源映射),但 Typescript 与 ES6 非常接近,以至于我倾向于捂住鼻子潜入 Typescript。 ES6/Typescript 很有趣,因为它允许您在定义接口(行为)时除了方法之外还包含属性。
          【解决方案11】:

          这是一个老问题,但这个话题一直困扰着我。

          由于这里和网络上的许多答案都集中在“强制执行”界面,我想提出另一种观点:

          当我使用多个行为相似的类(即实现一个接口)时,我最感受到缺乏接口。

          例如,我有一个 电子邮件生成器,它希望接收 电子邮件部分工厂,它“知道”如何生成部分的内容和 HTML。因此,它们都需要有某种getContent(id)getHtml(content) 方法。

          我能想到的最接近接口的模式(尽管它仍然是一种解决方法)是使用一个将获得 2 个参数的类,这将定义 2 个接口方法。

          这种模式的主要挑战是方法要么必须是static,要么必须将实例本身作为参数,才能访问其属性。但是,在某些情况下,我觉得这种权衡是值得的。

          class Filterable {
            constructor(data, { filter, toString }) {
              this.data = data;
              this.filter = filter;
              this.toString = toString;
              // You can also enforce here an Iterable interface, for example,
              // which feels much more natural than having an external check
            }
          }
          
          const evenNumbersList = new Filterable(
            [1, 2, 3, 4, 5, 6], {
              filter: (lst) => {
                const evenElements = lst.data.filter(x => x % 2 === 0);
                lst.data = evenElements;
              },
              toString: lst => `< ${lst.data.toString()} >`,
            }
          );
          
          console.log('The whole list:    ', evenNumbersList.toString(evenNumbersList));
          evenNumbersList.filter(evenNumbersList);
          console.log('The filtered list: ', evenNumbersList.toString(evenNumbersList));

          【讨论】:

            【解决方案12】:

            我知道这是一个旧的,但我最近发现自己越来越需要一个方便的 API 来检查对象与接口的关系。所以我写了这个:https://github.com/tomhicks/methodical

            也可以通过 NPM 获得:npm install methodical

            它基本上完成了上面建议的所有事情,有一些更严格的选项,而且所有这些都不需要做大量的if (typeof x.method === 'function') 样板。

            希望有人觉得它有用。

            【讨论】:

            • Tom,我刚刚看了一个 AngularJS TDD 视频,当他安装一个框架时,其中一个依赖包就是你的系统包!干得好!
            • 哈哈太好了。在工作人员确信 JavaScript 中的接口不可行后,我基本上放弃了它。最近我有了一个关于基本上代理对象的库的想法,以确保只使用某些方法,这基本上就是接口。我仍然认为接口在 JavaScript 中占有一席之地!顺便可以链接那个视频吗?我想看看。
            • 你打赌,汤姆。我会尽快找到它。也感谢关于接口作为代理的轶事。干杯!
            • 我认为 JS 中接口的挑战在于能够检查它们(如 paperObj instanceof WritableInterface)并在同一个对象上实现多个接口。是的,我也“仍然认为接口在 JavaScript 中占有一席之地”.
            【解决方案13】:

            我也很烦恼要找到一种解决方案来模拟具有较低影响的界面。

            一种解决方案可能是制作一个工具:

            /**
            @parameter {Array|object} required : method name list or members types by their name
            @constructor
            */
            let Interface=function(required){
                this.obj=0;
                if(required instanceof Array){
                    this.obj={};
                    required.forEach(r=>this.obj[r]='function');
                }else if(typeof(required)==='object'){
                    this.obj=required;
                }else {
                    throw('Interface invalid parameter required = '+required);
                }
            };
            /** check constructor instance
            @parameter {object} scope : instance to check.
            @parameter {boolean} [strict] : if true -> throw an error if errors ar found.
            @constructor
            */
            Interface.prototype.check=function(scope,strict){
                let err=[],type,res={};
                for(let k in this.obj){
                    type=typeof(scope[k]);
                    if(type!==this.obj[k]){
                        err.push({
                            key:k,
                            type:this.obj[k],
                            inputType:type,
                            msg:type==='undefined'?'missing element':'bad element type "'+type+'"'
                        });
                    }
                }
                res.success=!err.length;
                if(err.length){
                    res.msg='Class bad structure :';
                    res.errors=err;
                    if(strict){
                        let stk = new Error().stack.split('\n');
                        stk.shift();
                        throw(['',res.msg,
                            res.errors.map(e=>'- {'+e.type+'} '+e.key+' : '+e.msg).join('\n'),
                            '','at :\n\t'+stk.join('\n\t')
                        ].join('\n'));
            
                    }
                }
                return res;
            };
            

            使用示例:

            // create interface tool
            let dataInterface=new Interface(['toData','fromData']);
            // abstract constructor
            let AbstractData=function(){
                dataInterface.check(this,1);// check extended element
            };
            // extended constructor
            let DataXY=function(){
                AbstractData.apply(this,[]);
                this.xy=[0,0];
            };
            DataXY.prototype.toData=function(){
                return [this.xy[0],this.xy[1]];
            };
            
            // should throw an error because 'fromData' is missing
            let dx=new DataXY();
            

            有类

            class AbstractData{
                constructor(){
                    dataInterface.check(this,1);
                }
            }
            class DataXY extends AbstractData{
                constructor(){
                    super();
                    this.xy=[0,0];
                }
                toData(){
                    return [this.xy[0],this.xy[1]];
                }
            }
            

            还是有点性能消耗,需要依赖Interface类,但可以用于调试或打开api。

            【讨论】:

              【解决方案14】:

              这是旧的,但我实现了在没有转译器的情况下在 ES6 上使用的接口。

              https://github.com/jkutianski/ES6-Interfaces

              【讨论】:

                【解决方案15】:

                通过接口,您可以实现一种多态方式。 Javascript 确实 NOT 需要接口类型来处理这个和其他interface 的东西。为什么? Javascript 是一种动态类型的语言。以具有相同方法的类数组为例:

                Circle()
                Square()
                Triangle()
                

                如果您想了解多态性的工作原理,David Kruglinsky 的 Book MFC 非常棒(为 C++ 编写)

                在这些类中实现draw() 方法,将这些类的实例推送到数组中,并在循环中调用draw() 方法以迭代数组。这是完全有效的。你可以说你隐式地实现了abstract class。它在现实中不存在,但在你的脑海中你做到了,Javascript 没有问题。与真实接口的区别在于您必须实现所有接口方法,而在这种情况下不需要。

                你可以做很多事情!我认为需要有人写一本关于此类内容的书,因为如果您小心处理 Javascript,它会提供很多可能性。 ES6 摇滚!无需使用本地语言等进行 WebAssembly。Javascript 前景广阔。

                注意:由于 Javascript 的动态特性,Typescript 的使用是有问题的。为什么 f#ck 你会改变一种语言的性质。 C# 添加了 dynamic 关键字来做一些动态的事情,但不会使语言动态化。因为 Javascript 本质上是动态的,所以你很容易出错,但它的动态特性也是一个非常强的地方。它不是傻瓜语言,但对所有语言都有效。

                接口是一种契约。您将必须实现所有方法。只有通过静态方式才能做到这一点,这与 Typescript 的本质背道而驰。

                【讨论】:

                • 这个答案的前两段很棒。之后,它在反 TypeScript 和支持动态语言的咆哮中脱轨。
                • 这不是咆哮。我遵守 ES6。 Javascript 在设计上是动态的,为什么要在其上添加 typescript 的转换开销,我就是不明白。
                【解决方案16】:

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2021-01-25
                  • 2020-10-31
                  • 1970-01-01
                  • 2021-12-21
                  • 2021-03-25
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多