ES6 มีให้ใช้งานอย่างสมบูรณ์ในโหนด 4 ฉันสงสัยว่ามันมีแนวคิดของอินเทอร์เฟซเพื่อกำหนดสัญญาวิธีการMyClass implements MyInterface
หรือไม่
ฉันหาข้อมูลเกี่ยวกับ Googling ไม่ได้มากนัก แต่อาจมีเคล็ดลับหรือวิธีแก้ปัญหาที่ดี
ES6 มีให้ใช้งานอย่างสมบูรณ์ในโหนด 4 ฉันสงสัยว่ามันมีแนวคิดของอินเทอร์เฟซเพื่อกำหนดสัญญาวิธีการMyClass implements MyInterface
หรือไม่
ฉันหาข้อมูลเกี่ยวกับ Googling ไม่ได้มากนัก แต่อาจมีเคล็ดลับหรือวิธีแก้ปัญหาที่ดี
คำตอบ:
อินเทอร์เฟซไม่ได้เป็นส่วนหนึ่งของ ES6 แต่เป็นคลาส
ถ้าคุณต้องการจริงๆพวกคุณควรดูที่typescriptที่สนับสนุนพวกเขา
ในความคิดเห็น 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 );
เนื่องจาก ECMA เป็นภาษาที่ 'ไม่มีคลาส' การใช้องค์ประกอบแบบคลาสสิกจึงไม่สมเหตุสมผลในสายตาของฉัน อันตรายก็คือในการทำเช่นนี้คุณกำลังพยายามออกแบบภาษาใหม่อย่างมีประสิทธิภาพ (และหากมีใครรู้สึกอย่างมากเกี่ยวกับเรื่องนี้มีโซลูชันแบบองค์รวมที่ยอดเยี่ยมเช่น TypeScript ดังกล่าวที่ช่วยลดการสร้างวงล้อใหม่)
ตอนนี้ไม่ได้หมายความว่าการจัดองค์ประกอบนั้นไม่อยู่ในคำถามอย่างไรก็ตามใน Plain Old JS ฉันค้นคว้าเรื่องนี้เมื่อนานมาแล้ว ผู้สมัครที่แข็งแกร่งที่สุดที่ฉันเคยเห็นในการจัดการองค์ประกอบภายในกระบวนทัศน์ต้นแบบวัตถุคือการประทับตราซึ่งตอนนี้ฉันใช้ในโครงการต่างๆมากมาย และที่สำคัญมันเป็นไปตามข้อกำหนดที่ชัดเจน
ข้อมูลเพิ่มเติมเกี่ยวกับแสตมป์ที่นี่
class
struct
ภาษาคลาสสิกอย่าง Smalltalk? มันจะช่วยให้การขยายแบบไดนามิกของต้นแบบและแม้กระทั่งกรณี
นี่คือทางออกสำหรับปัญหาของฉัน คุณสามารถ 'ใช้งาน' อินเทอร์เฟซหลาย ๆ อินเทอร์เฟซได้โดยการแทนที่อินเทอร์เฟซหนึ่งกับอินเทอร์เฟซอื่น
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'
แก้ไข:
ฉันปรับปรุงโค้ดเพื่อให้ตอนนี้คุณสามารถใช้งาน (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'
มีแพ็คเกจที่สามารถจำลองอินเทอร์เฟซได้
คุณสามารถใช้ es6-interface