JavaScript มีประเภทอินเตอร์เฟส (เช่น 'ส่วนต่อประสาน' ของ Java) หรือไม่


324

ฉันเรียนรู้วิธีที่จะทำให้ OOP ด้วย JavaScript มันมีแนวคิดของส่วนต่อประสาน (เช่นของ Java interface) หรือไม่?

ดังนั้นฉันจะสามารถสร้างผู้ฟัง ...


18
สำหรับผู้ที่มองหาตัวเลือกเพิ่มเติมtypescriptจะมีอินเตอร์เฟซ
SD

2
อีกตัวเลือกหนึ่งถ้าคุณต้องการใช้วานิลลา JS คือimplement.jsดังที่แสดงไว้ที่นี่
Richard Lovell

คำตอบ:


649

ไม่มีความคิดของ "คลาสนี้จะต้องมีฟังก์ชั่นเหล่านี้" (นั่นคือไม่มีส่วนต่อประสาน se) เนื่องจาก:

  1. การสืบทอด JavaScript จะขึ้นอยู่กับวัตถุไม่ใช่คลาส นั่นไม่ใช่เรื่องใหญ่จนกระทั่งคุณตระหนัก:
  2. JavaScript เป็นอย่างยิ่งภาษาพิมพ์แบบไดนามิก - คุณสามารถสร้างวัตถุที่มีวิธีการที่เหมาะสมซึ่งจะทำให้มันเป็นไปตามอินเตอร์เฟซและจากนั้น undefine ทุกสิ่งที่ทำให้มันเป็นไปตาม มันง่ายมากที่จะทำลายระบบการพิมพ์ - แม้กระทั่งโดยไม่ตั้งใจ! - มันไม่คุ้มค่าที่จะลองสร้างระบบพิมพ์ตั้งแต่แรก

แต่ JavaScript ใช้สิ่งที่เรียกว่าเป็ดพิมพ์ (ถ้ามันเดินเหมือนเป็ดและ quacks เหมือนเป็ดเท่าที่ใส่ใจ JS ก็เป็นเป็ด) หากวัตถุของคุณมีการต้มตุ๋น (), เดิน () และวิธีการบิน () รหัสสามารถใช้มันได้ทุกที่ที่คาดหวัง วัตถุที่สามารถเดินเล่นต้มตุ๋นและบินได้โดยไม่ต้องใช้ส่วนต่อประสาน "Duckable" อินเทอร์เฟซคือชุดของฟังก์ชันที่รหัสใช้ (และค่าส่งคืนจากฟังก์ชันเหล่านั้น) และด้วยการพิมพ์เป็ดคุณจะได้รับสิ่งนั้นฟรี

ตอนนี้ที่ไม่ได้บอกรหัสของคุณจะไม่ล้มเหลวผ่านไปครึ่งทางถ้าคุณพยายามที่จะโทรsome_dog.quack(); คุณจะได้รับ TypeError ตรงไปตรงมาถ้าคุณบอกให้สุนัขต้มตุ๋นคุณมีปัญหาใหญ่กว่าเล็กน้อย การพิมพ์เป็ดนั้นทำได้ดีที่สุดเมื่อคุณเก็บเป็ดทั้งหมดไว้ในแถวเพื่อพูดและไม่ปล่อยให้สุนัขและเป็ดปะปนกันเว้นแต่คุณจะปฏิบัติต่อพวกมันเหมือนสัตว์ทั่วไป กล่าวอีกนัยหนึ่งแม้ว่าอินเทอร์เฟซเป็นของเหลว แต่ก็ยังคงมีอยู่ มันมักจะเป็นข้อผิดพลาดในการส่งสุนัขไปยังรหัสที่คาดว่าจะต้มตุ๋นและบินในสถานที่แรก

แต่ถ้าคุณแน่ใจว่าคุณกำลังทำสิ่งที่ถูกต้องคุณสามารถแก้ไขปัญหาสุนัขหลอกด้วยการทดสอบการมีอยู่ของวิธีการเฉพาะก่อนที่จะพยายามที่จะใช้มัน สิ่งที่ต้องการ

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

ดังนั้นคุณสามารถตรวจสอบวิธีการทั้งหมดที่คุณสามารถใช้ได้ก่อนที่จะใช้ แม้ว่าไวยากรณ์จะเป็นเรื่องที่น่าเกลียด มีวิธีที่สวยกว่าเล็กน้อย:

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

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

นี่คือ JavaScript มาตรฐานดังนั้นจึงควรใช้งานล่าม JS ที่ควรใช้ มันมีประโยชน์เพิ่มเติมของการอ่านเช่นภาษาอังกฤษ

สำหรับเบราว์เซอร์สมัยใหม่ (นั่นคือเบราว์เซอร์อื่น ๆ ที่ไม่ใช่ IE 6-8) ค่อนข้างมีวิธีที่จะป้องกันไม่ให้ทรัพย์สินปรากฏในfor...in:

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

ปัญหาคือวัตถุ IE7 ไม่มี.definePropertyเลยและใน IE8 นั้นถูกกล่าวหาว่าใช้งานได้เฉพาะบนวัตถุโฮสต์ (นั่นคือองค์ประกอบ DOM และเช่นนั้น) หากความเข้ากันได้เป็นปัญหาคุณจะไม่สามารถใช้งาน.definePropertyได้ (ฉันจะไม่พูดถึง IE6 เพราะมันค่อนข้างไม่เกี่ยวข้องอีกแล้วนอกประเทศจีน)

ปัญหาก็คือว่าบางรูปแบบการเข้ารหัสชอบที่จะถือว่าทุกคนที่เขียนรหัสที่ไม่ดีและห้ามแก้ไขในกรณีที่มีคนต้องการที่จะใช้สุ่มสี่สุ่มห้าObject.prototype for...inหากคุณสนใจหรือใช้รหัส (IMO ใช้งานไม่ได้ ) ให้ลองใช้รุ่นที่แตกต่างกันเล็กน้อย:

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

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

7
มันไม่น่ากลัวเท่าที่ควรจะเป็น for...inคือ - และถูกเสมอ - เต็มไปด้วยอันตรายดังกล่าวและใครก็ตามที่ทำอย่างนั้นโดยไม่พิจารณาว่ามีคนเพิ่มเข้ามาObject.prototype(ไม่ใช่เทคนิคที่ผิดปกติโดยการยอมรับของบทความนั้น) จะเห็นรหัสของพวกเขาอยู่ในมือของคนอื่น
cHao

1
@entonio: ฉันคิดว่าความสามารถในการแก้ไขประเภทในตัวเป็นคุณสมบัติแทนที่จะเป็นปัญหา มันเป็นส่วนสำคัญของสิ่งที่ทำให้ shims / polyfills เป็นไปได้ หากไม่มีมันเราก็จะห่อทุกประเภทในตัวด้วยประเภทย่อยที่อาจเข้ากันไม่ได้หรือรอการสนับสนุนเบราว์เซอร์สากล (ซึ่งอาจไม่เคยเกิดขึ้นเมื่อเบราว์เซอร์ไม่สนับสนุนสิ่งที่ทำให้คนไม่ใช้เพราะเบราว์เซอร์ไม่ ไม่สนับสนุนมัน) เนื่องจากประเภทในตัวสามารถปรับเปลี่ยนได้เราจึงสามารถเพิ่มฟังก์ชั่นมากมายที่ยังไม่มีอยู่
cHao

1
ใน Javascript เวอร์ชันล่าสุด (1.8.5) คุณสามารถกำหนดคุณสมบัติของวัตถุที่ไม่สามารถนับได้ วิธีนี้คุณสามารถหลีกเลี่ยงfor...inปัญหานี้ได้ developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
......

1
@ Tomás: น่าเสียดายที่เบราว์เซอร์ทุกตัวใช้งานบางอย่างที่รองรับ ES5 เรายังคงต้องกังวลเกี่ยวกับสิ่งนี้ และแม้แล้วว่า " for...inปัญหา" จะยังคงอยู่ในระดับหนึ่งสาเหตุที่มีจะเป็นรหัสเลอะเทอะ ... ดีนั้นและเป็นงานค่อนข้างน้อยกว่าเพียงแค่Object.defineProperty(obj, 'a', {writable: true, enumerable: false, value: 3}); obj.a = 3;ฉันสามารถเข้าใจคนที่ไม่พยายามทำบ่อยขึ้น : P
cHao

1
เฮ้ ... รัก "ตรงไปตรงมาถ้าคุณบอกให้สุนัขต้มตุ๋นคุณมีปัญหาใหญ่กว่าเล็กน้อยการเปรียบเทียบที่ยอดเยี่ยมเพื่อแสดงว่าภาษาไม่ควรพยายามหลีกเลี่ยงความโง่เขลานั่นคือการต่อสู้ที่หายไปเสมอ
โกพป้า com

72

รับสำเนาของ ' รูปแบบการออกแบบ JavaScriptโดยดัสตินดิแอซ มีบทไม่กี่บทที่ทุ่มเทให้กับการใช้งานอินเตอร์เฟส JavaScript ผ่าน Duck Typing มันเป็นการอ่านที่ดีเช่นกัน แต่ไม่มีไม่มีการดำเนินพื้นเมืองภาษาของอินเตอร์เฟซที่คุณต้องเป็ดประเภท

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

วิธีที่อธิบายไว้ในหนังสือ "รูปแบบการออกแบบโปร javascript" อาจเป็นวิธีที่ดีที่สุดของสิ่งที่ฉันได้อ่านที่นี่และจากสิ่งที่ฉันได้ลอง คุณสามารถใช้การสืบทอดด้านบนซึ่งจะทำให้ดียิ่งขึ้นในการทำตามแนวคิดของ OOP บางคนอาจอ้างว่าคุณไม่ต้องการแนวคิด OOP ใน JS แต่ฉันขอแตกต่างกัน
animageofmine

21

JavaScript (ECMAScript edition 3) ได้บันทึกimplementsคำสงวนไว้เพื่อใช้ในอนาคตเก็บไว้สำหรับใช้ในอนาคตฉันคิดว่าสิ่งนี้มีจุดประสงค์เพื่อจุดประสงค์นี้อย่างเร่งด่วนอย่างไรก็ตามเพื่อให้ได้สเปคออกไปนอกประตูพวกเขาไม่มีเวลากำหนดสิ่งที่ต้องทำกับมันดังนั้นในปัจจุบันเบราว์เซอร์ไม่ได้ทำอะไรนอกจาก ปล่อยให้มันนั่งอยู่ตรงนั้นและบางครั้งก็บ่นถ้าคุณพยายามใช้มันเพื่ออะไรบางอย่าง

เป็นไปได้และง่ายพอที่จะสร้างObject.implement(Interface)วิธีการของคุณเองด้วยตรรกะที่หยุดชะงักเมื่อใดก็ตามที่คุณสมบัติ / ฟังก์ชั่นชุดหนึ่งไม่ถูกนำไปใช้ในวัตถุที่กำหนด

ผมเขียนบทความเกี่ยวกับวัตถุปฐมนิเทศ ที่ใช้รูปแบบของตัวเองดังต่อไปนี้ :

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

มีหลายวิธีในการสกินแมวตัวนี้โดยเฉพาะ แต่นี่เป็นตรรกะที่ฉันใช้สำหรับการปรับใช้อินเตอร์เฟสของตัวเอง ฉันคิดว่าฉันชอบวิธีนี้และง่ายต่อการอ่านและใช้ (ตามที่คุณเห็นด้านบน) มันหมายถึงการเพิ่มวิธีการ 'นำไปใช้' Function.prototypeซึ่งบางคนอาจมีปัญหา แต่ฉันคิดว่ามันใช้งานได้อย่างสวยงาม

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
ไวยากรณ์นี้ทำร้ายสมองของฉันจริงๆ แต่การดำเนินการที่นี่ค่อนข้างน่าสนใจ
Cypher

2
Javascript ถูกผูกมัดให้ทำเช่นนั้น (ทำร้ายสมอง) เป็นพิเศษเมื่อมาจากการใช้ภาษา OO ที่สะอาดยิ่งขึ้น
Steven de Salas

10
@StevendeSalas: เอ๊ะ JS มีแนวโน้มที่จะค่อนข้างสะอาดเมื่อคุณหยุดพยายามที่จะใช้มันเป็นภาษาที่เน้นการเรียน ทั้งหมดอึที่จำเป็นในการเรียนเลียนแบบอินเตอร์เฟซ ฯลฯ ... นั่นคือสิ่งที่จะได้มันทำให้เจ็บสมองของคุณ ต้นแบบ? สิ่งที่เรียบง่ายจริงๆเมื่อคุณหยุดการต่อสู้พวกเขา
cHao

มีอะไรใน "// .. ตรวจสอบตรรกะของสมาชิก" ? มันมีลักษณะอย่างไร
PositiveGuy

สวัสดี @We ตรวจสอบสมาชิกตรรกะวิธีการวนลูปผ่านคุณสมบัติที่ต้องการและการขว้างปาข้อผิดพลาดหากหนึ่งจะหายไป .. var interf = arguments[i]; for (prop in interf) { if (this.prototype[prop] === undefined) { throw 'Member [' + prop + '] missing from class definition.'; }}บางสิ่งบางอย่างตามสายของ ดูที่ด้านล่างของลิงค์บทความสำหรับตัวอย่างที่ซับซ้อนมากขึ้น
Steven de Salas

12

ส่วนต่อประสาน JavaScript:

แม้ว่า JavaScript ไม่ได้มีinterfaceชนิดก็มักจะเป็นเวลาที่จำเป็น สำหรับเหตุผลที่เกี่ยวข้องกับลักษณะไดนามิกของ JavaScript และการใช้ Prototypical-Inheritance มันเป็นเรื่องยากที่จะมั่นใจได้ว่าอินเตอร์เฟซที่สอดคล้องกันในชั้นเรียน - อย่างไรก็ตามมันเป็นไปได้ที่จะทำเช่นนั้น และลอกเลียนแบบบ่อยครั้ง

ณ จุดนี้มีหลายวิธีที่จะเลียนแบบส่วนต่อประสานใน JavaScript ได้ ความแปรปรวนของวิธีการมักจะตอบสนองความต้องการบางอย่างในขณะที่คนอื่นไม่ได้รับภาระ บ่อยครั้งที่แนวทางที่แข็งแกร่งที่สุดนั้นยุ่งยากและมีอิทธิพลมากต่อผู้ดำเนินการ

นี่คือวิธีการในการเรียนอินเตอร์เฟซ / นามธรรมที่ไม่ยุ่งยากมากเป็นที่ชัดเจนทำให้การใช้งานภายใน Abstractions ให้น้อยที่สุดและออกจากห้องเพียงพอสำหรับวิธีการแบบไดนามิกหรือแบบกำหนดเอง:

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

ผู้เข้าร่วม

ศีล Resolver

resolvePreceptฟังก์ชั่นเป็นสาธารณูปโภคและผู้ช่วยฟังก์ชั่นการใช้งานภายในของคุณบทคัดย่อชั้น งานของมันคือการอนุญาตให้มีการปรับแต่งการใช้งานการจัดการของห่อหุ้มศีล (ข้อมูลและพฤติกรรม) มันสามารถโยนข้อผิดพลาดหรือคำเตือน - และ - กำหนดค่าเริ่มต้นให้กับคลาส Implementor

iAbstractClass

iAbstractClassกำหนดอินเตอร์เฟซที่จะใช้ วิธีการของมันสร้างข้อตกลงโดยนัยกับชั้น Implementor ของมัน อินเตอร์เฟซนี้ได้มอบหมายให้แต่ละศีลเดียวกัน namespace กฎเกณฑ์ที่แน่นอน - หรือ - เพื่ออะไรก็ตามศีล Resolverกลับมาทำงาน อย่างไรก็ตามข้อตกลงโดยปริยายแก้ไขเป็นบริบท - บทบัญญัติของ Implementor

implementor

Implementor เพียง 'ตกลง' กับอินเตอร์เฟซ ( iAbstractClassในกรณีนี้) และนำไปใช้โดยใช้สร้าง-หักหลังiAbstractClass.apply(this) : โดยการกำหนดข้อมูลและพฤติกรรมด้านบนแล้วทำการจี้คอนสตรัคเตอร์ของอินเทอร์เฟซ - ผ่านบริบทของแอพพลิเคชั่นไปยังอินเทอร์เฟซคอนสตรัคเตอร์ - เราสามารถตรวจสอบให้แน่ใจว่า

นี่เป็นวิธีที่ไม่ยุ่งยากซึ่งให้บริการกับทีมของฉัน & ฉันทำได้ดีมากสำหรับเวลาและโครงการต่าง ๆ อย่างไรก็ตามมีข้อ จำกัด บางประการ

ข้อเสีย

แม้ว่าสิ่งนี้จะช่วยให้การใช้งานซอฟต์แวร์ของคุณมีความสม่ำเสมอ แต่ก็ไม่ได้ใช้อินเทอร์เฟซที่แท้จริง - แต่เลียนแบบพวกเขา แม้ว่าคำจำกัดความค่าเริ่มต้นและคำเตือนหรือข้อผิดพลาดจะได้รับการอธิบายการอธิบายการใช้งานนั้นได้รับการบังคับใช้และยืนยันโดยนักพัฒนา (เช่นเดียวกับการพัฒนา JavaScript จำนวนมาก)

นี่เป็นวิธีที่ดีที่สุดสำหรับ"การเชื่อมต่อใน JavaScript"แต่ฉันชอบที่จะเห็นการแก้ไขต่อไปนี้:

  • ยืนยันประเภทผลตอบแทน
  • ยืนยันลายเซ็น
  • ตรึงวัตถุจากdeleteการกระทำ
  • การยืนยันสิ่งอื่นที่แพร่หลายหรือจำเป็นในความจำเพาะของชุมชน JavaScript

ที่กล่าวว่าฉันหวังว่าสิ่งนี้จะช่วยคุณได้มากเท่ากับที่มีทีมของฉันและฉัน


7

คุณต้องการอินเตอร์เฟสใน Java เนื่องจากถูกพิมพ์แบบสแตติกและควรรู้จักสัญญาระหว่างคลาสในระหว่างการคอมไพล์ ใน JavaScript มันแตกต่างกัน JavaScript ถูกพิมพ์แบบไดนามิก หมายความว่าเมื่อคุณได้รับวัตถุคุณสามารถตรวจสอบว่ามันมีวิธีการเฉพาะและเรียกมันว่า


1
ที่จริงแล้วคุณไม่ต้องการอินเทอร์เฟซใน Java มันเป็นความล้มเหลวที่ปลอดภัยเพื่อให้แน่ใจว่าวัตถุมี API ที่แน่นอนดังนั้นคุณสามารถสลับมันสำหรับการใช้งานอื่น ๆ
BGerrissen

3
ไม่จำเป็นจริง ๆ แล้วพวกเขาต้องการใน Java เพื่อให้สามารถสร้าง vtables สำหรับคลาสที่ใช้อินเทอร์เฟซในเวลารวบรวม การประกาศว่าคลาสใช้อินเทอร์เฟซสั่งให้คอมไพเลอร์สร้างโครงสร้างเล็ก ๆ ที่มีพอยน์เตอร์ไปยังเมธอดทั้งหมดที่จำเป็นโดยอินเตอร์เฟสนั้น มิฉะนั้นจะต้องส่งชื่อตามรันไทม์ (เช่นภาษาที่พิมพ์แบบไดนามิก)
munificent

ฉันไม่คิดว่าถูกต้อง การจัดส่งเป็นแบบไดนามิกในภาษาจาวาเสมอ (เว้นแต่จะเป็นวิธีการสุดท้าย) และความจริงที่ว่าวิธีการเป็นของส่วนต่อประสานจะไม่เปลี่ยนกฎการค้นหา อินเทอร์เฟซเหตุผลมีความจำเป็นในภาษาที่มีการพิมพ์แบบคงที่ดังนั้นคุณสามารถใช้ 'pseudo-type' เดียวกัน (อินเทอร์เฟซ) เพื่ออ้างอิงคลาสที่ไม่เกี่ยวข้อง
entonio

2
@entonio: การจัดส่งไม่เป็นแบบไดนามิกอย่างที่เห็น วิธีการที่เกิดขึ้นจริงมักจะไม่เป็นที่รู้จักกันในขณะนี้ขอบคุณ polymorphism แต่ bytecode ไม่ได้พูดว่า "เรียกใช้วิธีการของคุณ"; มันบอกว่า "เรียกใช้ Superclass.yourMethod" JVM ไม่สามารถเรียกใช้เมธอดโดยไม่ทราบว่าคลาสใดที่จะมองหามันในระหว่างการเชื่อมโยงมันอาจใส่yourMethodที่ # 5 รายการในSuperclassvtable ของและสำหรับแต่ละคลาสย่อยที่มีของตัวเองyourMethodเพียงชี้รายการของคลาสย่อยที่ 5 ในการดำเนินการที่เหมาะสม
cHao

1
@entonio: สำหรับอินเตอร์เฟสกฎจะเปลี่ยนแปลงเล็กน้อย (ไม่ใช่ภาษาเดียว แต่กระบวนการเข้ารหัสที่สร้างขึ้นและกระบวนการค้นหาของ JVM นั้นแตกต่างกัน) คลาสที่ชื่อImplementationที่ใช้SomeInterfaceไม่เพียง แต่กล่าวว่ามันใช้อินเทอร์เฟซทั้งหมด มันมีข้อมูลที่ระบุว่า "ผมใช้SomeInterface.yourMethod" Implementation.yourMethodและจุดที่นิยามวิธีการ เมื่อโทร JVM จะมีลักษณะในชั้นเรียนสำหรับข้อมูลเกี่ยวกับการใช้งานของวิธีการอินเตอร์เฟซที่และพบว่าจะต้องมีการโทรSomeInterface.yourMethod Implementation.yourMethod
cHao

6

หวังว่าทุกคนที่ยังคงมองหาคำตอบจะเป็นประโยชน์

คุณสามารถลองใช้พร็อกซี (เป็นมาตรฐานตั้งแต่ 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;
    }
});

จากนั้นคุณสามารถพูดได้อย่างง่ายดาย:

myMap = {}
myMap.position = latLngLiteral;

5

เมื่อคุณต้องการใช้ transcompiler คุณสามารถลองใช้ TypeScript ได้ สนับสนุนคุณลักษณะ ECMA ฉบับร่าง (ในข้อเสนออินเทอร์เฟซที่เรียกว่า " โปรโตคอล ") คล้ายกับภาษาที่ชอบ coffeescript หรือ babel

ใน TypeScript อินเทอร์เฟซของคุณสามารถมีลักษณะดังนี้:

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"
}

คุณทำอะไรไม่ได้:


3

ไม่มีอินเตอร์เฟสดั้งเดิมใน JavaScript มีหลายวิธีในการจำลองอินเตอร์เฟส ฉันได้เขียนแพ็คเกจที่ทำ

คุณสามารถดูการฝังที่นี่


2

Javascript ไม่มีส่วนต่อประสาน แต่มันสามารถพิมพ์เป็ด, ตัวอย่างสามารถพบได้ที่นี่:

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


ฉันชอบรูปแบบที่บทความในลิงก์นั้นใช้เพื่อยืนยันเกี่ยวกับประเภท เกิดข้อผิดพลาดเมื่อบางสิ่งไม่ได้ใช้วิธีที่ควรจะเป็นอย่างที่ฉันคาดไว้และฉันชอบวิธีจัดกลุ่มวิธีการที่จำเป็นเหล่านี้ร่วมกัน (เช่นอินเทอร์เฟซ) ถ้าฉันทำแบบนี้
Eric Dubé

1
ฉันเกลียดการ transpiling (และซอร์สซอร์สสำหรับการดีบัก) แต่ typescript นั้นใกล้เคียงกับ ES6 มากจนฉันอยากจะจับจมูกและดำดิ่งลงไปใน typescript ES6 / typescript น่าสนใจเพราะช่วยให้คุณสามารถรวมคุณสมบัติได้นอกเหนือจากวิธีการต่าง ๆ เมื่อกำหนดส่วนต่อประสาน (ลักษณะการทำงาน)
Reinsbrain

1

ฉันรู้ว่านี่เป็นรุ่นเก่า แต่เมื่อเร็ว ๆ นี้ฉันพบว่าตัวเองต้องการมากขึ้นเพื่อให้มี API ที่ใช้งานง่ายสำหรับการตรวจสอบวัตถุกับส่วนต่อประสาน ดังนั้นฉันจึงเขียนสิ่งนี้: https://github.com/tomhicks/methodical

มันยังมีให้ผ่าน NPM: npm install methodical

โดยพื้นฐานแล้วมันทำทุกอย่างที่แนะนำข้างต้นโดยมีตัวเลือกบางอย่างสำหรับการเข้มงวดมากขึ้นเล็กน้อยและทั้งหมดนี้โดยไม่ต้องทำif (typeof x.method === 'function')หม้อไอน้ำจำนวนมาก

หวังว่าบางคนจะพบว่ามีประโยชน์


ทอมฉันเพิ่งดูวิดีโอ AngularJS TDD และเมื่อเขาติดตั้งเฟรมเวิร์กหนึ่งในแพ็กเกจที่ขึ้นต่อกันคือแพ็คเกจที่เป็นระเบียบของคุณ! เยี่ยมมาก!
โคดี้

ฮ่าฮ่าเยี่ยมมาก ฉันทิ้งมันหลังจากที่คนในที่ทำงานทำให้ฉันเชื่อมั่นว่าส่วนต่อประสานใน JavaScript ไม่ต้องไปทำงาน เมื่อเร็ว ๆ นี้ฉันมีความคิดเกี่ยวกับห้องสมุดที่โดยพื้นฐานแล้วผู้รับมอบฉันทะวัตถุเพื่อให้แน่ใจว่ามีเพียงวิธีการบางอย่างที่ใช้กับมันซึ่งเป็นสิ่งที่อินเตอร์เฟซ ฉันยังคิดว่าส่วนต่อประสานมีสถานที่ใน JavaScript! คุณสามารถลิงค์วิดีโอนั้นได้ไหม? ฉันอยากจะดู
Tom

คุณพนันทอม ฉันจะลองหาดูในไม่ช้า ขอบคุณเกร็ดเล็กเกร็ดน้อยเกี่ยวกับอินเตอร์เฟสเป็นพร็อกซี่เช่นกัน ไชโย!
โคดี้

1

นี่เป็นคำถามเก่า แต่หัวข้อนี้ไม่หยุดที่จะบักฉัน

จากคำตอบมากมายที่นี่และทั่วทั้งเว็บให้ความสำคัญกับ "การบังคับใช้" อินเทอร์เฟซฉันต้องการแนะนำมุมมองทางเลือก:

ฉันรู้สึกว่าไม่มีอินเทอร์เฟซมากที่สุดเมื่อฉันใช้หลายคลาสที่ทำงานคล้ายกัน (เช่นใช้อินเทอร์เฟซ )

ตัวอย่างเช่นฉันมีเครื่องมือสร้างอีเมลที่คาดว่าจะได้รับส่วนโรงงานอีเมลซึ่ง "รู้" ถึงวิธีสร้างเนื้อหาและ HTML ของส่วน ดังนั้นพวกเขาทุกคนต้องมีการจัดเรียงของบางส่วนgetContent(id)และgetHtml(content)วิธีการ

รูปแบบที่ใกล้เคียงที่สุดกับอินเตอร์เฟส (แม้ว่ามันจะยังคงเป็นวิธีแก้ปัญหา) ฉันคิดว่าใช้คลาสที่จะได้รับ 2 อาร์กิวเมนต์ซึ่งจะกำหนดวิธีการอินเทอร์เฟซ 2

ความท้าทายหลักของรูปแบบนี้ก็คือวิธีการนั้นจะต้องมีstaticหรือเพื่อให้เป็นอาร์กิวเมนต์ตัวอินสแตนซ์ของตัวเองเพื่อที่จะเข้าถึงคุณสมบัติของมัน อย่างไรก็ตามมีหลายกรณีที่ฉันพบว่าการแลกเปลี่ยนนี้มีความยุ่งยาก

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

ส่วนต่อประสานที่เป็นนามธรรมเช่นนี้

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

สร้างตัวอย่าง:

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

และใช้มัน

let x = new MyType()
x.print()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.