ES6 / Node 4에서 인터페이스를 만드는 방법이 있습니까?


110

ES6는 Node 4에서 완전히 사용할 수 있습니다.에서와 같이 메서드 계약을 정의하는 인터페이스 개념이 포함되어 있는지 궁금합니다 MyClass implements MyInterface.

내 인터넷 검색으로 많은 것을 찾을 수 없지만 사용 가능한 좋은 트릭이나 해결 방법이있을 수 있습니다.


2
충분히? 단연코.
Bergi

1
JS는 여전히 덕 타이핑을 사용합니다 . 정적으로 시행되는 "방법 계약"이 없습니다. 동적으로 테스트하려면 인터페이스 검사기를 쉽게 작성할 수 있습니다.
Bergi

26
파티에 늦었지만 질문이 주제에서 벗어났습니다. OP는 예상되는 기능이 있는지 확인을 원합니다. 클래스에 대한 새롭고 단순화 된 구문은 오래 기한이 지났으며 널리 사용될 것입니다. 그러나 인터페이스는 매우 좋은 이유로 다른 언어에서 일반적입니다. 저도 인터페이스가 ES2015의 일부가 아니라는 사실에 놀랐고 실망했습니다. 이것이 일반적인 발견 일 가능성이 있으므로 IMHO에 제안 된 해결 방법이 있는지 묻는 것이 합리적이지 않습니다.

9
도대체 어떻게이 주제에서 벗어 났습니까? 인터페이스는 제품이 아닌 프로그래밍 기술입니다. 이 질문은 유효하며 Java와 같은 클래스 정의를 가져 오는 ECMA Script 6 릴리스와 함께 좋은 질문입니다. 이 주제를 마치는 것은 이해 부족과 스택 오버플로에서 포인트 시스템이 능력과 어떻게 관련이 없는지를 보여줍니다.
Andrew S

4
문자 그대로 OP는이 질문에서 책, 도구, 소프트웨어 라이브러리, 자습서 또는 기타 오프 사이트 리소스를 추천하거나 찾도록 요청 하지 않습니다 .
Liam

답변:


90

인터페이스는 ES6의 일부가 아니지만 클래스는 있습니다.

정말로 필요한 경우 이를 지원하는 TypeScript 를 살펴보아야 합니다 .


1
"그들"은 인터페이스입니다. FWIW 위에 제공된 트랜스 파일러의 링크를주의 깊게 고려해야 할 수도 있습니다. 내가 예상했던대로는 아니지만 가깝습니다.

참고 : TypeScript의 순수한 인터페이스를 아는 한 아무것도 변환하지 않습니다. 그것들을 사용하는 경우에만 트랜스 파일 된 코드에 특정 논리가 있습니다.
Daniel Danielecki

9

의견에서 debiasej는 아래에 언급 된 기사에서 디자인 패턴 (인터페이스, 클래스 기반)에 대해 더 자세히 설명합니다.

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

자바 스크립트로 된 디자인 패턴 책도 유용 할 수 있습니다.

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는 시간을 할애 할 가치가 있습니다.
Jay Edwards

-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의 강점 IMHO를 더 잘 반영합니다. stampit.js.org 는 클래스와의 차이점에 대한 좋은 요약을 제공합니다. 궁극적으로 더 실용적인 방법론입니다.
Jay Edwards

1
그렇다면 "클래스 언어" 란 무엇입니까? C ++? class의 동의어 일뿐입니다 struct. Smalltalk와 같은 진정한 고전 언어? 이 프로토 타입 심지어 인스턴스를 동적으로 확장 할 수 있습니다
ᆼ ᆺ ᆼ

그것은 합리적인 요점입니다. 클래스 언어는 본질적으로 OOP 인 언어로 정의합니다. MDN에서 : "JavaScript는 프로토 타입 기반, 다중 패러다임, 동적 언어로 객체 지향, 명령형 및 선언적 (예 : 함수형 프로그래밍) 스타일을 지원합니다." google.com/url?sa=t&source=web&rct=j&url=https://…
Jay Edwards

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'

편집하다:

이제 코드를 개선하여 extend에서 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
당신의 대답의 문제는 그가 그것을 할 "도구"를 요구하지 않았다는 것입니다. 그러나 그것이 어떻게 이루어 졌는지 queni는 그 형식이 어떻게했는지 설명하는 대답이 더 정확했을 것입니다.
Gianfrancesco Aurecchia
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.