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

时间:2015-09-17 09:27:48

标签: javascript node.js interface ecmascript-6

ES6在Node 4中完全可用。我想知道它是否包含定义方法契约的接口概念,如MyClass implements MyInterface中所示。

我的谷歌搜索找不到多少,但也许有一个很好的技巧或解决方法。

6 个答案:

答案 0 :(得分:77)

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

如果您确实需要它们,请查看TypeScript support them

答案 1 :(得分:9)

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

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

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

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

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

ES6 JS中的工厂模式示例(运行:node 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 );

答案 2 :(得分:2)

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

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'</code>

EDIT:

I improved the code so you now can simply use implement(baseClass, interface1, interface2, ...) in the extend.

/**
* 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'

// 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'</code>

答案 3 :(得分:0)

有些软件包可以模拟接口。

您可以使用es6-interface

答案 4 :(得分:0)

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

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

有关邮票here的更多信息

答案 5 :(得分:0)

Flow允许interface specification,而不必将整个代码库都转换为TypeScript。

接口是way of breaking dependencies,但在现有代码中要谨慎操作。