有没有办法在ES6 /节点4中创建接口?


110

ES6在Node 4中完全可用。我想知道它是否包含接口的概念来定义方法协定(如)MyClass implements MyInterface

我在Google搜索中找不到很多,但也许有不错的技巧或解决方法。



1
JS仍然使用鸭子输入。没有静态执行的“方法合同”。如果要动态测试它们,则可以轻松编写自己的接口检查器。
Bergi 2015年

26
晚会晚了,但是不同意这个问题。OP希望确认是否存在预期功能。类的新的,简化的语法早就应该使用,并且可能会被广泛使用。但是由于非常好的理由,接口在其他语言中很常见。我也很惊讶和失望,因为学习界面不是ES2015的一部分。鉴于这可能是一个常见的发现,恕我直言,询问是否存在建议的解决方法并非没有道理。

9
这离主题有多远?接口是编程技术,不是产品。这个问题是有效的,而且随着ECMA Script 6的发布带来了类似类定义的Java,这是一个很好的问题。我认为本主题的结尾表明缺乏理解,并且在堆栈溢出时积分系统与能力之间没有关联。
安德鲁·S

4
实际上,OP在任何问题上都没有(建议)我们推荐或找到书籍,工具,软件库,教程或其他非现场资源
利亚姆'18

Answers:


90

接口不是ES6的一部分,但类是。

如果你真的需要的话,你应该看看打字稿支持他们


1
“他们”是接口。FWIW您可能需要仔细考虑以上提供的转换器的链接。不完全符合我的预期,但接近。

注意:据我所知,TypeScript中的纯接口没有任何内容。只有使用它们,转译的代码才具有一定的逻辑。
Daniel Danielecki

9

在评论中debiasej写了以下提到的文章,详细解释了有关设计模式(基于接口,类):

http://loredanacirstea.github.io/es6-design-patterns/

用javascript设计模式书可能对您也有用:

http://addyosmani.com/resources/essentialjsdesignpatterns/book/

设计模式=类+接口或多重继承

ES6 JS中的工厂模式示例(要运行:节点example.js):

"use strict";

// Types.js - Constructors used behind the scenes

// A constructor for defining new cars
class Car {
  constructor(options){
    console.log("Creating Car...\n");
    // some defaults
    this.doors = options.doors || 4;
    this.state = options.state || "brand new";
    this.color = options.color || "silver";
  }
}

// A constructor for defining new trucks
class Truck {
  constructor(options){
    console.log("Creating Truck...\n");
    this.state = options.state || "used";
    this.wheelSize = options.wheelSize || "large";
    this.color = options.color || "blue";
  }
}


// FactoryExample.js

// Define a skeleton vehicle factory
class VehicleFactory {}

// Define the prototypes and utilities for this factory

// Our default vehicleClass is Car
VehicleFactory.prototype.vehicleClass = Car;

// Our Factory method for creating new Vehicle instances
VehicleFactory.prototype.createVehicle = function ( options ) {

  switch(options.vehicleType){
    case "car":
      this.vehicleClass = Car;
      break;
    case "truck":
      this.vehicleClass = Truck;
      break;
    //defaults to VehicleFactory.prototype.vehicleClass (Car)
  }

  return new this.vehicleClass( options );

};

// Create an instance of our factory that makes cars
var carFactory = new VehicleFactory();
var car = carFactory.createVehicle( {
            vehicleType: "car",
            color: "yellow",
            doors: 6 } );

// Test to confirm our car was created using the vehicleClass/prototype Car

// Outputs: true
console.log( car instanceof Car );

// Outputs: Car object of color "yellow", doors: 6 in a "brand new" state
console.log( car );

var movingTruck = carFactory.createVehicle( {
                      vehicleType: "truck",
                      state: "like new",
                      color: "red",
                      wheelSize: "small" } );

// Test to confirm our truck was created with the vehicleClass/prototype Truck

// Outputs: true
console.log( movingTruck instanceof Truck );

// Outputs: Truck object of color "red", a "like new" state
// and a "small" wheelSize
console.log( movingTruck );

34
那么我可以在这里与其他人组合的接口在哪里?
Dmitri Zaitsev


2
此站点上的ES5模式到ES6有了很大的更新:loredanacirstea.github.io/es6-design-patterns
debiasej

8

既然ECMA是一种“无类”语言,那么在我看来,实现古典合成就没有多大意义。这样做的危险是,您正在有效地尝试重新设计语言(并且,如果对此有强烈的感觉,则可以使用出色的整体解决方案,例如前面提到的TypeScript来减轻对轮子的改造)。

现在,这并不是说构成是不可能的,但是在Plain Old JS中。我前一段时间对此进行了详细研究。我在对象原型范式中处理合成的最强候选人是Stampit,我现在在广泛的项目中使用它。而且,重要的是,它遵循明确规定的规范。

有关邮票的更多信息,请点击此处


1
即使是-1,我也坚持我的职位。可悲的是,有时候这就是SO的民主。我希望有人觉得这些链接有用。Stampit值得您花时间。
杰伊·爱德华兹

-1并非最终裁决。您的帖子可能以+ 100 / -1结尾。但是我仍然认为它是模糊的。JS不再不是“无类”的了。我怀疑“古典构成”在大多数情况下也不会被理解为您的意思:继承。(考虑整个继承与合成圣战。)还不清楚“ Plain Old JS”是什么。ES5?尽管语法更加冗长,但它支持了如今更广泛的技术,例如 “真正的”混入。邮票看起来很有趣,与混搭相比有哪些优势?
ᆼ ᆺ ᆼ

class关键字是语法糖。JS(ES ^ 6或其他)不是类语言。它只是装饰了ES5中的传统函数构造器方法。因此,“普通的旧JS”愉快地定义了ES的任何JS实现。坦白地说,我希望尚未做出决定来进一步巩固quora.com/Are-ES6-classes-bad-for-JavaScript语言中的类概念。邮票更好地体现了JS的优点恕我直言。stampit.js.org很好地总结了类之间的差异。最终,这是一种更为实用的方法。
杰伊·爱德华兹

1
但是,什么是“类语言”?C ++?class只是的同义词struct。像Smalltalk这样真正经典的语言?它允许动态扩展原型甚至实例
ᆼᆺᆼ

这是一个合理的观点。我将类语言定义为本质上是OOP的语言。来自MDN:“ JavaScript是一种基于原型的,多范式的动态语言,支持面向对象,命令式和声明式(例如函数式编程)样式。” google.com/url?sa=t&source=web&rct=j&url=https://...
杰伊·爱德华兹

6

这是我解决这个问题的方法。您可以通过覆盖一个接口与另一个接口来“实现”多个接口。

class MyInterface {
    // Declare your JS doc in the Interface to make it acceable while writing the Class and for later inheritance
    /**
     * Gives the sum of the given Numbers
     * @param {Number} a The first Number
     * @param {Number} b The second Number
     * @return {Number} The sum of the Numbers
     */
    sum(a, b) { this._WARNING('sum(a, b)'); }


    // delcare a warning generator to notice if a method of the interface is not overridden
    // Needs the function name of the Interface method or any String that gives you a hint ;)
    _WARNING(fName='unknown method') {
        console.warn('WARNING! Function "'+fName+'" is not overridden in '+this.constructor.name);
    }
}

class MultipleInterfaces extends MyInterface {
    // this is used for "implement" multiple Interfaces at once
    /**
     * Gives the square of the given Number
     * @param {Number} a The Number
     * @return {Number} The square of the Numbers
     */
    square(a) { this._WARNING('square(a)'); }
}

class MyCorrectUsedClass extends MyInterface {
    // You can easy use the JS doc declared in the interface
    /** @inheritdoc */
    sum(a, b) {
        return a+b;
    }
}
class MyIncorrectUsedClass extends MyInterface {
    // not overriding the method sum(a, b)
}

class MyMultipleInterfacesClass extends MultipleInterfaces {
    // nothing overriden to show, that it still works
}


let working = new MyCorrectUsedClass();

let notWorking = new MyIncorrectUsedClass();

let multipleInterfacesInstance = new MyMultipleInterfacesClass();

// TEST IT

console.log('working.sum(1, 2) =', working.sum(1, 2));
// output: 'working.sum(1, 2) = 3'

console.log('notWorking.sum(1, 2) =', notWorking.sum(1, 2));
// output: 'notWorking.sum(1, 2) = undefined'
// but also sends a warn to the console with 'WARNING! Function "sum(a, b)" is not overridden in MyIncorrectUsedClass'

console.log('multipleInterfacesInstance.sum(1, 2) =', multipleInterfacesInstance.sum(1, 2));
// output: 'multipleInterfacesInstance.sum(1, 2) = undefined'
// console warn: 'WARNING! Function "sum(a, b)" is not overridden in MyMultipleInterfacesClass'

console.log('multipleInterfacesInstance.square(2) =', multipleInterfacesInstance.square(2));
// output: 'multipleInterfacesInstance.square(2) = undefined'
// console warn: 'WARNING! Function "square(a)" is not overridden in MyMultipleInterfacesClass'

编辑:

我改进了代码,因此您现在可以在扩展中简单地使用Implement(baseClass,interface1,interface2,...)。

/**
* Implements any number of interfaces to a given class.
* @param cls The class you want to use
* @param interfaces Any amount of interfaces separated by comma
* @return The class cls exteded with all methods of all implemented interfaces
*/
function implement(cls, ...interfaces) {
    let clsPrototype = Object.getPrototypeOf(cls).prototype;
    for (let i = 0; i < interfaces.length; i++) {
        let proto = interfaces[i].prototype;
        for (let methodName of Object.getOwnPropertyNames(proto)) {
            if (methodName!== 'constructor')
                if (typeof proto[methodName] === 'function')
                    if (!clsPrototype[methodName]) {
                        console.warn('WARNING! "'+methodName+'" of Interface "'+interfaces[i].name+'" is not declared in class "'+cls.name+'"');
                        clsPrototype[methodName] = proto[methodName];
                    }
        }
    }
    return cls;
}

// Basic Interface to warn, whenever an not overridden method is used
class MyBaseInterface {
    // declare a warning generator to notice if a method of the interface is not overridden
    // Needs the function name of the Interface method or any String that gives you a hint ;)
    _WARNING(fName='unknown method') {
        console.warn('WARNING! Function "'+fName+'" is not overridden in '+this.constructor.name);
    }
}


// create a custom class
/* This is the simplest example but you could also use
*
*   class MyCustomClass1 extends implement(MyBaseInterface) {
*       foo() {return 66;}
*   }
*
*/
class MyCustomClass1 extends MyBaseInterface {
    foo() {return 66;}
}

// create a custom interface
class MyCustomInterface1 {
     // Declare your JS doc in the Interface to make it acceable while writing the Class and for later inheritance

    /**
     * Gives the sum of the given Numbers
     * @param {Number} a The first Number
     * @param {Number} b The second Number
     * @return {Number} The sum of the Numbers
     */
    sum(a, b) { this._WARNING('sum(a, b)'); }
}

// and another custom interface
class MyCustomInterface2 {
    /**
     * Gives the square of the given Number
     * @param {Number} a The Number
     * @return {Number} The square of the Numbers
     */
    square(a) { this._WARNING('square(a)'); }
}

// Extend your custom class even more and implement the custom interfaces
class AllInterfacesImplemented extends implement(MyCustomClass1, MyCustomInterface1, MyCustomInterface2) {
    /**
    * @inheritdoc
    */
    sum(a, b) { return a+b; }

    /**
    * Multiplies two Numbers
    * @param {Number} a The first Number
    * @param {Number} b The second Number
    * @return {Number}
    */
    multiply(a, b) {return a*b;}
}


// TEST IT

let x = new AllInterfacesImplemented();

console.log("x.foo() =", x.foo());
//output: 'x.foo() = 66'

console.log("x.square(2) =", x.square(2));
// output: 'x.square(2) = undefined
// console warn: 'WARNING! Function "square(a)" is not overridden in AllInterfacesImplemented'

console.log("x.sum(1, 2) =", x.sum(1, 2));
// output: 'x.sum(1, 2) = 3'

console.log("x.multiply(4, 5) =", x.multiply(4, 5));
// output: 'x.multiply(4, 5) = 20'

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.