JavaScript có loại giao diện (chẳng hạn như 'giao diện' của Java) không?


324

Tôi đang học cách tạo OOP bằng JavaScript . Nó có khái niệm giao diện (như Java interface) không?

Vì vậy, tôi sẽ có thể tạo ra một người nghe ...


18
Đối với những người tìm kiếm nhiều tùy chọn hơn, TypeScript không có giao diện .
SD

2
Một tùy chọn khác nếu bạn muốn sử dụng vani JS là implement.js , như chứng minh đây
Richard Lovell

Câu trả lời:


649

Không có khái niệm "lớp này phải có các chức năng này" (nghĩa là không có giao diện nào), bởi vì:

  1. Kế thừa JavaScript dựa trên các đối tượng, không phải các lớp. Đó không phải là một vấn đề lớn cho đến khi bạn nhận ra:
  2. JavaScript là một ngôn ngữ được gõ cực kỳ linh hoạt - bạn có thể tạo một đối tượng với các phương thức thích hợp, điều này sẽ làm cho nó phù hợp với giao diện, và sau đó xác định tất cả những thứ khiến nó phù hợp . Thật dễ dàng để lật đổ hệ thống loại - thậm chí vô tình! - rằng sẽ không đáng để thử và tạo một hệ thống loại ngay từ đầu.

Thay vào đó, JavaScript sử dụng cái được gọi là gõ vịt . (Nếu nó đi như một con vịt và quạ giống như một con vịt, theo như JS quan tâm, thì đó là một con vịt.) Nếu đối tượng của bạn có các phương thức quack (), walk () và fly (), mã có thể sử dụng nó bất cứ nơi nào nó mong đợi một đối tượng có thể đi bộ, lang băm và bay, mà không yêu cầu thực hiện một số giao diện "Duckable". Giao diện chính xác là tập hợp các hàm mà mã sử dụng (và các giá trị trả về từ các hàm đó) và với cách gõ vịt, bạn sẽ có được điều đó miễn phí.

Bây giờ, điều đó không có nghĩa là mã của bạn sẽ không bị lỗi giữa chừng, nếu bạn cố gắng gọi some_dog.quack(); bạn sẽ nhận được TypeError. Thành thật mà nói, nếu bạn bảo chó đi lang thang, bạn có vấn đề lớn hơn một chút; Gõ vịt hoạt động tốt nhất khi bạn giữ tất cả các con vịt của bạn liên tiếp, có thể nói, và không để chó và vịt hòa nhập với nhau trừ khi bạn coi chúng là động vật chung. Nói cách khác, mặc dù giao diện trôi chảy, nó vẫn ở đó; thường là một lỗi khi chuyển một con chó sang mã mà hy vọng nó sẽ quẫy và bay ngay từ đầu.

Nhưng nếu bạn chắc chắn rằng mình đang làm đúng, bạn có thể giải quyết vấn đề về con chó bằng cách kiểm tra sự tồn tại của một phương pháp cụ thể trước khi thử sử dụng nó. Cái gì đó như

if (typeof(someObject.quack) == "function")
{
    // This thing can quack
}

Vì vậy, bạn có thể kiểm tra tất cả các phương pháp bạn có thể sử dụng trước khi sử dụng chúng. Cú pháp là loại xấu, mặc dù. Có một cách hay hơn một chút:

Object.prototype.can = function(methodName)
{
     return ((typeof this[methodName]) == "function");
};

if (someObject.can("quack"))
{
    someObject.quack();
}

Đây là JavaScript tiêu chuẩn, do đó, nó sẽ hoạt động trong bất kỳ trình thông dịch JS nào đáng sử dụng. Nó có thêm lợi ích của việc đọc như tiếng Anh.

Đối với các trình duyệt hiện đại (nghĩa là, hầu như bất kỳ trình duyệt nào khác ngoài IE 6-8), thậm chí còn có một cách để giữ cho tài sản không hiển thị trong for...in:

Object.defineProperty(Object.prototype, 'can', {
    enumerable: false,
    value: function(method) {
        return (typeof this[method] === 'function');
    }
}

Vấn đề là các đối tượng IE7 hoàn toàn không có .defineProperty, và trong IE8, nó được cho là chỉ hoạt động trên các đối tượng máy chủ (nghĩa là các phần tử DOM, v.v.). Nếu khả năng tương thích là một vấn đề, bạn không thể sử dụng .defineProperty. (Tôi thậm chí sẽ không đề cập đến IE6, vì nó không còn liên quan nữa bên ngoài Trung Quốc.)

Một vấn đề khác là một số phong cách mã hóa muốn cho rằng mọi người đều viết mã xấu và cấm sửa đổi Object.prototypetrong trường hợp ai đó muốn sử dụng một cách mù quáng for...in. Nếu bạn quan tâm đến điều đó hoặc đang sử dụng mã (IMO bị hỏng ), hãy thử một phiên bản hơi khác:

function can(obj, methodName)
{
     return ((typeof obj[methodName]) == "function");
}

if (can(someObject, "quack"))
{
    someObject.quack();
}

7
Nó không kinh khủng như nó được tạo ra. for...inlà - và luôn luôn - đầy rẫy những nguy hiểm như vậy, và bất cứ ai làm điều đó mà không ít nhất là xem xét rằng ai đó đã thêm vào Object.prototype(một kỹ thuật không phổ biến, bởi sự thừa nhận của chính bài báo đó) sẽ thấy mã của họ bị phá vỡ trong tay người khác.
cHao

1
@entonio: Tôi coi tính linh hoạt của các loại tích hợp là một tính năng chứ không phải là một vấn đề. Đó là một phần lớn của những gì làm cho shims / polyfill khả thi. Nếu không có nó, chúng tôi sẽ gói tất cả các loại tích hợp với các kiểu con có thể không tương thích hoặc chờ hỗ trợ trình duyệt phổ quát (điều này có thể không bao giờ đến, khi trình duyệt không hỗ trợ công cụ khiến mọi người không sử dụng vì trình duyệt không ' t ủng hộ nó). Vì các loại tích hợp có thể được sửa đổi, thay vào đó chúng ta có thể chỉ cần thêm nhiều chức năng chưa tồn tại.
cHao

1
Trong phiên bản cuối cùng của Javascript (1.8.5), bạn có thể định nghĩa thuộc tính của một đối tượng là không thể đếm được. Bằng cách này bạn có thể tránh được for...invấn đề. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
Khăn

1
@ Tomás: Đáng buồn thay, cho đến khi mọi trình duyệt đang chạy một cái gì đó tương thích với ES5, chúng ta vẫn phải lo lắng về những thứ như thế này. Và thậm chí sau đó, " for...invấn đề" vẫn sẽ tồn tại ở một mức độ nào đó, bởi vì sẽ luôn có mã cẩu thả ... tốt, điều đó, và Object.defineProperty(obj, 'a', {writable: true, enumerable: false, value: 3});còn khá nhiều công việc hơn chỉ là obj.a = 3;. Tôi hoàn toàn có thể hiểu mọi người không cố gắng làm điều đó thường xuyên hơn. : P
cHao

1
Hehe ... yêu "Thành thật mà nói, nếu bạn bảo chó đi lang thang, bạn có vấn đề lớn hơn một chút. Tương tự như vậy để chứng minh rằng các ngôn ngữ không nên cố gắng tránh sự ngu ngốc. Đó luôn là một trận chiến thua. -Scott
Skooppa. com

72

Chọn một bản sao ' Mẫu thiết kế JavaScript ' của Dustin Diaz . Có một vài chương dành riêng để thực hiện giao diện JavaScript thông qua Duck Typing. Đó là một đọc tốt đẹp là tốt. Nhưng không, không có ngôn ngữ thực thi giao diện gốc, bạn phải sử dụng Duck Type .

// example duck typing method
var hasMethods = function(obj /*, method list as strings */){
    var i = 1, methodName;
    while((methodName = arguments[i++])){
        if(typeof obj[methodName] != 'function') {
            return false;
        }
    }
    return true;
}

// in your code
if(hasMethods(obj, 'quak', 'flapWings','waggle')) {
    //  IT'S A DUCK, do your duck thang
}

Phương pháp được mô tả trong cuốn sách "các mẫu thiết kế javascript chuyên nghiệp" có lẽ là cách tiếp cận tốt nhất trong số những điều tôi đã đọc ở đây và từ những gì tôi đã thử. Bạn có thể sử dụng tính kế thừa trên đầu trang, điều này làm cho việc theo dõi các khái niệm OOP trở nên tốt hơn nữa. Một số người có thể cho rằng bạn không cần các khái niệm OOP trong JS, nhưng tôi xin khác.
animageofmine

21

JavaScript (ECMAScript phiên bản 3) có một implementstừ dành riêng được lưu lại để sử dụng trong tương lai . Tôi nghĩ rằng điều này được dự định chính xác cho mục đích này, tuy nhiên, trong lúc vội vã đưa thông số kỹ thuật ra khỏi cửa, họ không có thời gian để xác định phải làm gì với nó, vì vậy, tại thời điểm hiện tại, các trình duyệt không làm gì ngoài hãy để nó ngồi đó và thỉnh thoảng phàn nàn nếu bạn cố gắng sử dụng nó cho một cái gì đó.

Có thể và thực sự đủ dễ dàng để tạo Object.implement(Interface)phương thức của riêng bạn với logic mà baulks mỗi khi một tập hợp các thuộc tính / hàm cụ thể không được triển khai trong một đối tượng nhất định.

Tôi đã viết một bài viết về hướng đối tượng trong đó sử dụng ký hiệu của riêng tôi như sau :

// Create a 'Dog' class that inherits from 'Animal'
// and implements the 'Mammal' interface
var Dog = Object.extend(Animal, {
    constructor: function(name) {
        Dog.superClass.call(this, name);
    },
    bark: function() {
        alert('woof');
    }
}).implement(Mammal);

Có nhiều cách để lột con mèo đặc biệt này, nhưng đây là logic tôi đã sử dụng để thực hiện Giao diện của riêng mình. Tôi thấy tôi thích cách tiếp cận này, và nó rất dễ đọc và sử dụng (như bạn có thể thấy ở trên). Điều đó có nghĩa là thêm một phương thức 'hiện thực' Function.prototypemà một số người có thể gặp vấn đề, nhưng tôi thấy nó hoạt động rất tốt.

Function.prototype.implement = function() {
    // Loop through each interface passed in and then check 
    // that its members are implemented in the context object (this).
    for(var i = 0; i < arguments.length; i++) {
       // .. Check member's logic ..
    }
    // Remember to return the class being tested
    return this;
}

4
Cú pháp này thực sự làm tổn thương não của tôi, nhưng việc thực hiện ở đây khá thú vị.
Cypher

2
Javascript nhất định phải làm điều đó (làm tổn thương não) đặc biệt khi đến từ việc triển khai ngôn ngữ OO sạch hơn.
Steven de Salas

10
@StevendeSalas: Ơ. JS thực sự có xu hướng khá sạch sẽ khi bạn ngừng cố gắng coi nó như một ngôn ngữ hướng đến lớp. Tất cả những thứ nhảm nhí cần có để mô phỏng các lớp học, giao diện, v.v ... đó là những gì sẽ thực sự khiến não bạn bị tổn thương. Nguyên mẫu? Thứ đơn giản, thực sự, một khi bạn ngừng chiến đấu với chúng.
cHao

những gì trong "// .. Kiểm tra logic của thành viên." ? Điều đó giống như thế nào?
positiveGuy

Xin chào @ Chúng tôi, kiểm tra logic thành viên có nghĩa là lặp qua các thuộc tính mong muốn và đưa ra lỗi nếu thiếu .. một cái gì đó dọc theo dòng var interf = arguments[i]; for (prop in interf) { if (this.prototype[prop] === undefined) { throw 'Member [' + prop + '] missing from class definition.'; }}. Xem dưới cùng của liên kết bài viết cho một ví dụ chi tiết hơn.
Steven de Salas

12

Giao diện JavaScript:

Mặc dù JavaScript khônginterfaceloại, nhưng nó thường cần thời gian. Vì các lý do liên quan đến bản chất động của JavaScript và việc sử dụng Nguyên mẫu-Kế thừa, rất khó để đảm bảo các giao diện nhất quán giữa các lớp - tuy nhiên, có thể làm như vậy; và thường xuyên thi đua.

Tại thời điểm này, có một số cách cụ thể để mô phỏng Giao diện trong JavaScript; phương sai trong các phương pháp thường thỏa mãn một số nhu cầu, trong khi những phương pháp khác không được giải quyết. Thông thường, cách tiếp cận mạnh mẽ nhất là quá cồng kềnh và cản trở người thực hiện (nhà phát triển).

Đây là một cách tiếp cận với Giao diện / Các lớp trừu tượng không quá cồng kềnh, mang tính khám phá, giữ cho việc triển khai bên trong Trừu tượng đến mức tối thiểu và để đủ chỗ cho các phương pháp động hoặc tùy chỉnh:

function resolvePrecept(interfaceName) {
    var interfaceName = interfaceName;
    return function curry(value) {
        /*      throw new Error(interfaceName + ' requires an implementation for ...');     */
        console.warn('%s requires an implementation for ...', interfaceName);
        return value;
    };
}

var iAbstractClass = function AbstractClass() {
    var defaultTo = resolvePrecept('iAbstractClass');

    this.datum1 = this.datum1 || defaultTo(new Number());
    this.datum2 = this.datum2 || defaultTo(new String());

    this.method1 = this.method1 || defaultTo(new Function('return new Boolean();'));
    this.method2 = this.method2 || defaultTo(new Function('return new Object();'));

};

var ConcreteImplementation = function ConcreteImplementation() {

    this.datum1 = 1;
    this.datum2 = 'str';

    this.method1 = function method1() {
        return true;
    };
    this.method2 = function method2() {
        return {};
    };

    //Applies Interface (Implement iAbstractClass Interface)
    iAbstractClass.apply(this);  // .call / .apply after precept definitions
};

Những người tham gia

Giải quyết giới

Các resolvePreceptchức năng là một tiện ích & helper chức năng để sử dụng bên trong của Abstract class . Công việc của nó là cho phép thực hiện tùy chỉnh - xử lý các Giới luật được đóng gói (dữ liệu & hành vi) . Nó có thể đưa ra lỗi hoặc cảnh báo - VÀ - gán giá trị mặc định cho lớp Người triển khai.

i sát thương

Các iAbstractClassđịnh nghĩa giao diện sẽ được sử dụng. Cách tiếp cận của nó đòi hỏi một thỏa thuận ngầm với lớp Người thực hiện. Giao diện này gán từng giới cho cùng một không gian tên chính xác - HOẶC - cho bất cứ điều gì mà hàm Precolver trả về. Tuy nhiên, thỏa thuận ngầm giải quyết theo bối cảnh - một điều khoản của Người thực hiện.

Người thực hiện

Implementor chỉ đơn giản là 'đồng ý' với một giao diện ( iAbstractClass trong trường hợp này) và áp dụng nó bằng cách sử dụng Constructor-Hijacking : iAbstractClass.apply(this). Bằng cách xác định dữ liệu & hành vi ở trên, sau đó chiếm quyền điều khiển của Trình tạo giao diện - chuyển ngữ cảnh của Người triển khai đến Trình tạo giao diện - chúng tôi có thể đảm bảo rằng phần ghi đè của Người triển khai sẽ được thêm vào và Giao diện sẽ giải thích các cảnh báo và giá trị mặc định.

Đây là một cách tiếp cận không cồng kềnh đã phục vụ nhóm của tôi và tôi rất tốt cho quá trình thời gian và các dự án khác nhau. Tuy nhiên, nó có một số cảnh báo và nhược điểm.

Hạn chế

Mặc dù điều này giúp triển khai tính nhất quán trong toàn bộ phần mềm của bạn ở một mức độ đáng kể, nhưng nó không thực hiện các giao diện thực sự - nhưng mô phỏng chúng. Mặc dù các định nghĩa, mặc định và cảnh báo hoặc lỗi được giải thích, việc khám phá sử dụng được nhà phát triển thực thi & khẳng định (cũng như phần lớn sự phát triển JavaScript).

Đây dường như là cách tiếp cận tốt nhất cho "Giao diện trong JavaScript" , tuy nhiên, tôi rất muốn thấy các giải pháp sau:

  • Xác nhận các loại trả lại
  • Xác nhận chữ ký
  • Đóng băng các đối tượng từ deletehành động
  • Các xác nhận của bất kỳ điều gì khác phổ biến hoặc cần thiết trong tính đặc thù của cộng đồng JavaScript

Điều đó nói rằng, tôi hy vọng điều này sẽ giúp bạn nhiều như nó có đội của tôi và tôi.


7

Bạn cần các giao diện trong Java vì nó được gõ tĩnh và hợp đồng giữa các lớp nên được biết trong quá trình biên dịch. Trong JavaScript thì khác. JavaScript được gõ động; điều đó có nghĩa là khi bạn nhận được đối tượng, bạn có thể kiểm tra xem nó có phương thức cụ thể nào không và gọi nó.


1
Trên thực tế, bạn không cần các giao diện trong Java, thật không an toàn để đảm bảo các đối tượng có một API nhất định để bạn có thể trao đổi chúng cho các triển khai khác.
BGerrissen

3
Không, chúng thực sự cần thiết trong Java để nó có thể xây dựng vtables cho các lớp thực hiện giao diện vào thời gian biên dịch. Khai báo rằng một lớp thực hiện một giao diện hướng dẫn trình biên dịch xây dựng một cấu trúc nhỏ có chứa các con trỏ tới tất cả các phương thức cần thiết cho giao diện đó. Nếu không, nó sẽ phải gửi theo tên trong thời gian chạy (giống như các ngôn ngữ được gõ động).
khoan hồng

Tôi không nghĩ đó là chính xác. Dispatch luôn động trong java (trừ khi có thể một phương thức là cuối cùng) và thực tế là phương thức đó thuộc về một giao diện không thay đổi các quy tắc tra cứu. Các giao diện lý do là cần thiết trong các ngôn ngữ gõ tĩnh là vì vậy bạn có thể sử dụng cùng loại 'giả' (giao diện) để chỉ các lớp không liên quan.
entonio

2
@entonio: Công văn không năng động như vẻ ngoài của nó. Phương thức thực tế thường không được biết đến thời gian chạy, nhờ vào tính đa hình, nhưng mã byte không nói "gọi yourMethod"; nó nói "gọi Superclass.yourMethod". JVM không thể gọi một phương thức mà không biết nên tìm lớp nào. Trong quá trình liên kết, nó có thể đặt yourMethodở mục số 5 trong Superclassvtable của nó và đối với mỗi lớp con có riêng nó yourMethod, chỉ cần chỉ ra mục nhập của lớp con # 5 lúc thực hiện phù hợp.
cHao

1
@entonio: Đối với các giao diện, quy tắc làm thay đổi một chút. (Không phải theo ngôn ngữ, nhưng mã byte được tạo và quá trình tra cứu của JVM là khác nhau.) Một lớp có tên Implementationthực SomeInterfacehiện không chỉ nói rằng nó thực hiện toàn bộ giao diện. Nó có thông tin cho biết "Tôi triển khai SomeInterface.yourMethod" và chỉ ra định nghĩa phương thức cho Implementation.yourMethod. Khi JVM gọi SomeInterface.yourMethod, nó tìm trong lớp để biết thông tin về việc triển khai phương thức của giao diện đó và thấy nó cần gọi Implementation.yourMethod.
cHao

6

Hy vọng rằng bất cứ ai vẫn đang tìm kiếm một câu trả lời đều thấy nó hữu ích.

Bạn có thể dùng thử Proxy (Đó là tiêu chuẩn kể từ ECMAScript 2015): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy

latLngLiteral = new Proxy({},{
    set: function(obj, prop, val) {
        //only these two properties can be set
        if(['lng','lat'].indexOf(prop) == -1) {
            throw new ReferenceError('Key must be "lat" or "lng"!');
        }

        //the dec format only accepts numbers
        if(typeof val !== 'number') {
            throw new TypeError('Value must be numeric');
        }

        //latitude is in range between 0 and 90
        if(prop == 'lat'  && !(0 < val && val < 90)) {
            throw new RangeError('Position is out of range!');
        }
        //longitude is in range between 0 and 180
        else if(prop == 'lng' && !(0 < val && val < 180)) {
            throw new RangeError('Position is out of range!');
        }

        obj[prop] = val;

        return true;
    }
});

Sau đó, bạn có thể dễ dàng nói:

myMap = {}
myMap.position = latLngLiteral;

5

Khi bạn muốn sử dụng một trình biên dịch, thì bạn có thể dùng thử TypeScript. Nó hỗ trợ dự thảo các tính năng ECMA (trong đề xuất, các giao diện được gọi là " giao thức ") tương tự như các ngôn ngữ như coffeescript hoặc babel làm.

Trong TypeScript, giao diện của bạn có thể trông như sau:

interface IMyInterface {
    id: number; // TypeScript types are lowercase
    name: string;
    callback: (key: string; value: any; array: string[]) => void;
    type: "test" | "notATest"; // so called "union type"
}

Những gì bạn không thể làm:


3

không có giao diện gốc trong JavaScript, có một số cách để mô phỏng giao diện. tôi đã viết một gói mà làm điều đó

bạn có thể thấy cấy ghép ở đây


2

Javascript không có giao diện. Nhưng nó có thể được gõ vịt, một ví dụ có thể được tìm thấy ở đây:

http://reinsbrain.blogspot.com/2008/10/interface-in-javascript.html


Tôi thích mẫu mà bài viết tại liên kết đó sử dụng để xác nhận về loại. Một lỗi được đưa ra khi một cái gì đó không thực hiện phương thức mà nó được cho là chính xác như tôi mong đợi và tôi thích làm thế nào tôi có thể nhóm các phương thức cần thiết này lại với nhau (như một giao diện) nếu tôi làm theo cách này.
Eric Dubé

1
Tôi ghét dịch mã (và bản đồ nguồn để gỡ lỗi) nhưng Bản mô tả rất gần với ES6 đến nỗi tôi có xu hướng giữ mũi và đi sâu vào Bản in. ES6 / Typecript rất thú vị vì nó cho phép bạn bao gồm các thuộc tính bên cạnh các phương thức khi xác định giao diện (hành vi).
Reinsbrain

1

Tôi biết đây là một cái cũ, nhưng gần đây tôi thấy mình cần nhiều hơn nữa để có một API tiện dụng để kiểm tra các đối tượng dựa trên các giao diện. Vì vậy, tôi đã viết điều này: https://github.com/tomhicks/methodical

Nó cũng có sẵn thông qua NPM: npm install methodical

Về cơ bản, nó thực hiện mọi thứ được đề xuất ở trên, với một số tùy chọn để nghiêm ngặt hơn một chút, và tất cả mà không phải thực hiện vô số if (typeof x.method === 'function')nồi hơi.

Hy vọng ai đó thấy nó hữu ích.


Tom, tôi vừa xem một video AngularJS TDD và khi anh ấy cài đặt một khung, một trong những gói phụ thuộc là gói có phương pháp của bạn! Làm tốt lắm!
Cody

Haha xuất sắc. Về cơ bản, tôi đã từ bỏ nó sau khi mọi người làm việc thuyết phục tôi giao diện trong JavaScript là không có. Gần đây, tôi đã có một ý tưởng về một thư viện về cơ bản là một đối tượng để đảm bảo rằng chỉ có một số phương thức nhất định được sử dụng trên đó, về cơ bản là giao diện là gì. Tôi vẫn nghĩ rằng các giao diện có một vị trí trong JavaScript! Bạn có thể liên kết video đó bằng cách này? Tôi muốn xem qua.
Tom

Bạn đặt cược, Tom. Tôi sẽ cố gắng tìm nó sớm. Thx các giai thoại về giao diện là proxy, quá. Chúc mừng!
Cody

1

Đây là một câu hỏi cũ, tuy nhiên chủ đề này không bao giờ hết lỗi với tôi.

Vì nhiều câu trả lời ở đây và trên web tập trung vào "thi hành" giao diện, tôi muốn đề xuất một chế độ xem khác:

Tôi cảm thấy thiếu giao diện nhiều nhất khi tôi sử dụng nhiều lớp hoạt động tương tự nhau (tức là thực hiện một giao diện ).

Ví dụ: tôi có Trình tạo email dự kiến ​​sẽ nhận được các phần của Email , "biết" cách tạo nội dung và HTML của các phần. Do đó, tất cả chúng cần phải có một số loại getContent(id)getHtml(content)phương pháp.

Mẫu gần nhất với các giao diện (mặc dù nó vẫn là một cách giải quyết) Tôi có thể nghĩ đến việc sử dụng một lớp sẽ nhận được 2 đối số, sẽ xác định 2 phương thức giao diện.

Thách thức chính với mẫu này là các phương thức hoặc phải statichoặc lấy đối số của chính nó để truy cập các thuộc tính của nó. Tuy nhiên, có những trường hợp tôi thấy sự đánh đổi này đáng để phiền phức.

class Filterable {
  constructor(data, { filter, toString }) {
    this.data = data;
    this.filter = filter;
    this.toString = toString;
    // You can also enforce here an Iterable interface, for example,
    // which feels much more natural than having an external check
  }
}

const evenNumbersList = new Filterable(
  [1, 2, 3, 4, 5, 6], {
    filter: (lst) => {
      const evenElements = lst.data.filter(x => x % 2 === 0);
      lst.data = evenElements;
    },
    toString: lst => `< ${lst.data.toString()} >`,
  }
);

console.log('The whole list:    ', evenNumbersList.toString(evenNumbersList));
evenNumbersList.filter(evenNumbersList);
console.log('The filtered list: ', evenNumbersList.toString(evenNumbersList));


0

giao diện trừu tượng như thế này

const MyInterface = {
  serialize: () => {throw "must implement serialize for MyInterface types"},
  print: () => console.log(this.serialize())
}

tạo một ví dụ:

function MyType() {
  this.serialize = () => "serialized "
}
MyType.prototype = MyInterface

và sử dụng nó

let x = new MyType()
x.print()
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.