Có cách nào để tạo giao diện trong ES6 / Node 4 không?


110

ES6 hoàn toàn có sẵn trong Node 4. Tôi đã tự hỏi liệu nó có bao gồm một khái niệm về giao diện để xác định các hợp đồng phương thức như trong không MyClass implements MyInterface.

Tôi không thể tìm thấy nhiều thứ với Google của mình, nhưng có thể có một thủ thuật hoặc cách giải quyết hay.



1
JS vẫn sử dụng kiểu gõ vịt . Không có "hợp đồng phương thức" được thực thi tĩnh nào. Nếu bạn muốn kiểm tra chúng động, bạn có thể dễ dàng viết trình kiểm tra giao diện của riêng mình.
Bergi

26
Đến bữa tiệc muộn, nhưng không đồng ý câu hỏi là lạc đề. OP muốn xác nhận nếu một tính năng mong đợi tồn tại. Cú pháp mới, được đơn giản hóa, cho các lớp đã quá hạn từ lâu và có thể sẽ được sử dụng rộng rãi. Nhưng các giao diện phổ biến trong các ngôn ngữ khác vì lý do rất chính đáng. Tôi cũng rất ngạc nhiên và thất vọng khi tìm hiểu các giao diện không phải là một phần của ES2015. Cho rằng đây có thể là một khám phá phổ biến, IMHO không phải là vô lý khi hỏi liệu có một giải pháp được đề xuất hay không.

9
Làm thế quái nào mà chuyện này lại lạc đề? Giao diện là kỹ thuật lập trình không phải là một sản phẩm. Câu hỏi hợp lệ và là một câu hỏi hay với việc phát hành ECMA Script 6 mang đến các định nghĩa lớp giống như Java. Tôi nghĩ rằng việc kết thúc chủ đề này cho thấy sự thiếu hiểu biết và cách hệ thống điểm tràn Stack không tương quan với khả năng.
Andrew S

4
Thực sự không có điểm nào OP (yêu cầu) chúng tôi giới thiệu hoặc tìm một cuốn sách, công cụ, thư viện phần mềm, hướng dẫn hoặc tài nguyên ngoài trang web khác trong bất kỳ câu hỏi nào trong số này.
Liam

Câu trả lời:


90

Các giao diện không phải là một phần của ES6 mà là các lớp.

Nếu bạn thực sự cần đến chúng, bạn nên xem xét nguyên cảohỗ trợ họ .


1
"chúng" là giao diện. FWIW Bạn có thể cần phải xem xét cẩn thận liên kết cho trình vận chuyển được cung cấp ở trên. Không chính xác như tôi mong đợi, nhưng gần.

Một lưu ý: theo như tôi biết giao diện thuần túy trong TypeScript chuyển thành không có gì. Chỉ khi bạn sử dụng chúng thì mã được chuyển có một logic nhất định.
Daniel Danielecki

9

Trong ý kiến, debiasej đã viết bài báo được đề cập bên dưới giải thích thêm về các mẫu thiết kế (dựa trên giao diện, lớp):

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

Sách mẫu thiết kế bằng javascript cũng có thể hữu ích cho bạn:

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

Mẫu thiết kế = các lớp + giao diện hoặc đa kế thừa

Một ví dụ về mẫu ban đầu trong ES6 JS (để chạy: 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
Vậy đâu là giao diện mà tôi có thể soạn thảo với người khác?
Dmitri Zaitsev

Một số giải thích sâu hơn là tại trang web này: sitepoint.com/object-oriented-javascript-deep-dive-es6-classes
42n4

2
Có một bản cập nhật tuyệt vời của các mẫu ES5 cho ES6 tại trang web này: loredanacirstea.github.io/es6-design-patterns
debiasej

8

Cho rằng ECMA là một ngôn ngữ 'không có lớp học', việc triển khai bố cục cổ điển - trong mắt tôi - không có nhiều ý nghĩa. Điều nguy hiểm là, khi làm như vậy, bạn đang cố gắng thiết kế lại ngôn ngữ một cách hiệu quả (và, nếu ai đó cảm thấy mạnh mẽ về điều đó, thì có các giải pháp toàn diện tuyệt vời như TypeScript đã nói ở trên giúp giảm thiểu việc phát minh lại bánh xe)

Tuy nhiên, điều đó không có nghĩa là bố cục nằm ngoài câu hỏi trong Plain Old JS. Tôi đã nghiên cứu điều này một thời gian dài trước đây. Ứng cử viên nặng ký nhất mà tôi từng thấy để xử lý bố cục trong mô hình nguyên mẫu đối tượng là temit , hiện tôi đang sử dụng trong nhiều dự án. Và, quan trọng là, nó tuân thủ một đặc điểm kỹ thuật rõ ràng.

thêm thông tin về tem tại đây


1
Tôi đứng trước bài viết của mình ngay cả với -1. Đáng buồn thay, đó là dân chủ của SO đôi khi. Tôi hy vọng ai đó tìm thấy các liên kết hữu ích. Stampit là giá trị thời gian của bạn.
Jay Edwards

-1 không phải là phán quyết cuối cùng. Bài đăng của bạn có thể kết thúc + 100 / -1. Tuy nhiên tôi vẫn nghĩ nó mơ hồ. JS không còn là "class-free" nữa. Tôi nghi ngờ rằng "Thành phần cổ điển" cũng sẽ không được hiểu theo ý của bạn: kế thừa. (Xem xét toàn bộ phần thừa kế so với thánh chiến thành phần.) Cũng không rõ "Plain Old JS" là gì. ES5? Mặc dù với cú pháp dài dòng hơn, nó hỗ trợ các kỹ thuật phổ biến hơn hiện nay, chẳng hạn như trộn lẫn "true" . Những con tem trông thật thú vị, chúng có ưu điểm gì hơn so với bản trộn?
ᆼ ᆺ ᆼ

từ khóa lớp là đường cú pháp. JS - ES ^ 6 hoặc cách khác - không phải là ngôn ngữ lớp. nó chỉ đơn thuần trang trí cách tiếp cận hàm tạo hàm truyền thống trong ES5. Do đó, "JS đơn giản cũ" định nghĩa một cách vui vẻ bất kỳ triển khai JS nào của ES. Thành thật mà nói, tôi ước rằng quyết định không được đưa ra để tiếp tục thu hút ý tưởng về lớp trong ngôn ngữ quora.com/Are-ES6-classes-bad-for-JavaScript. Tem phản ánh tốt hơn điểm mạnh của JS IMHO. temit.js.org đưa ra một bản tóm tắt tốt về sự khác biệt giữa các lớp. Cuối cùng, đó là một phương pháp thực dụng hơn.
Jay Edwards

1
Nhưng sau đó, "ngôn ngữ lớp học" là gì? C ++? classchỉ là một từ đồng nghĩa với struct. Một ngôn ngữ thực sự cổ điển như Smalltalk? Nó cho phép mở rộng động của các nguyên mẫu và thậm chí cả các phiên bản
ᆼ ᆺ ᆼ

Đó là một điểm hợp lý. Tôi sẽ định nghĩa một ngôn ngữ lớp là một ngôn ngữ về bản chất là OOP. Từ MDN: "JavaScript là ngôn ngữ động dựa trên nguyên mẫu, đa mô hình, hỗ trợ các kiểu hướng đối tượng, mệnh lệnh và khai báo (ví dụ: lập trình chức năng)." google.com/url?sa=t&source=web&rct=j&url=https://…
Jay Edwards

6

Đây là giải pháp của tôi cho vấn đề. Bạn có thể 'triển khai' nhiều giao diện bằng cách ghi đè một Giao diện này với một Giao diện khác.

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'

BIÊN TẬP:

Tôi đã cải thiện mã để bây giờ bạn có thể đơn giản sử dụng triển khai (baseClass, interface1, interface2, ...) trong phần mở rộng.

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

có các gói có thể mô phỏng giao diện.

bạn có thể sử dụng giao diện es6


2
vấn đề với câu trả lời của bạn là anh ta đã không yêu cầu một "công cụ" để làm điều đó. nhưng nó đã được thực hiện như thế nào thì queni sẽ đúng hơn là một câu trả lời sẽ giải thích cách thức mà hình thức đó thực hiện.
Gianfrancesco Aurecchia
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.