ES6 /ノード4でインターフェースを作成する方法はありますか?


109

ES6はノード4で完全に利用できます。ES6には、のようにメソッドコントラクトを定義するためのインターフェイスの概念が含まれているかどうか疑問に思いましたMyClass implements MyInterface

グーグルで多くを見つけることはできませんが、素晴らしいトリックや回避策があるかもしれません。


2
完全に?断然。
Bergi

1
JSはまだダックタイピングを使用しています。静的に適用される「メソッドコントラクト」はありません。それらを動的にテストする場合は、独自のインターフェイスチェッカーを簡単に作成できます。
ベルギ

26
パーティーに遅れますが、質問はトピックから外れています。期待される機能が存在する場合、OPは確認を求めます。クラスの新しい単純化された構文は長い間遅れており、広く使用される可能性があります。しかし、非常に正当な理由により、他の言語ではインターフェースが一般的です。私も、インターフェースを学ぶことに驚いて失望しました。ES2015の一部ではありません。これは一般的な発見である可能性が高いため、IMHOは、推奨される回避策があるかどうかを尋ねることは不合理ではありません。

9
これは一体どういうことなのでしょうか?インターフェースは製品ではなくプログラミング手法です。質問は有効であり、Javaのようなクラス定義を導入するECMA Script 6のリリースで良い質問です。このトピックの締めくくりは、理解の欠如と、スタックオーバーフローでポイントシステムが能力と相関しないことを示していると思います。
Andrew S

4
文字通り、OPは、この質問のいずれにおいても、本、ツール、ソフトウェアライブラリ、チュートリアル、またはその他のオフサイトリソースを推奨または検索するように(要求)しません
リアム

回答:


89

インターフェイスはES6の一部ではありませんが、クラスは一部です。

それらが本当に必要な場合は、それらサポートするTypeScriptを調べる必要があります


1
「それら」はインターフェースです。FWIW上記のトランスパイラーのリンクを注意深く検討する必要がある場合があります。予想どおりではないが、近い。

注:私が知っている限り、TypeScriptの純粋なインターフェイスは何にも変換されません。それらを使用する場合にのみ、トランスパイルされたコードには特定のロジックがあります。
Daniel Danielecki

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 );

34
では、他のユーザーと構成できるインターフェイスはどこにあるのでしょうか。
Dmitri Zaitsev

より深い説明はこのサイトにあります:sitepoint.com/object-oriented-javascript-deep-dive-es6-classes
42n4

2
このサイトでES6にES5パターンの大きなアップデートがあります。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'

0

インターフェイスをシミュレートできるパッケージがあります。

あなたはes6-interfaceを使うことができ ます


2
あなたの答えの問題は、彼がそれを行うための「ツール」を求めなかったことです。しかし、それがどのように行われたかは、その形式がそれをどのようにしたかを綴るより正確な答えだったでしょう。
Gianfrancesco Aurecchia
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.