Ich lerne wie man OOP mit JavaScript macht . Verfügt es über ein Schnittstellenkonzept (wie das von Java interface
) ?
Ich könnte also einen Hörer erstellen...
Ich lerne wie man OOP mit JavaScript macht . Verfügt es über ein Schnittstellenkonzept (wie das von Java interface
) ?
Ich könnte also einen Hörer erstellen...
Es gibt keine Vorstellung von "diese Klasse muss diese Funktionen haben" (d.h. keine Schnittstellen per se), denn:
Stattdessen verwendet JavaScript das so genannte Duckmäuserschreiben . (Wenn es wie eine Ente läuft und wie eine Ente quakt, ist es für JS eine Ente.) Wenn Ihr Objekt über die Methoden quack(), walk() und fly() verfügt, kann der Code es überall dort verwenden, wo ein Objekt erwartet wird, das laufen, quaken und fliegen kann, ohne dass eine "Duckable"-Schnittstelle implementiert werden muss. Die Schnittstelle ist genau die Menge der Funktionen, die der Code verwendet (und die Rückgabewerte dieser Funktionen), und mit der Duck-Typisierung erhalten Sie das umsonst.
Das heißt aber nicht, dass Ihr Code nicht auf halbem Weg scheitern wird, wenn Sie versuchen, die some_dog.quack()
; Sie erhalten einen TypeError. Ehrlich gesagt, wenn Sie Hunden sagen, dass sie quaken sollen, haben Sie etwas größere Probleme; die Eingabe von Enten funktioniert am besten, wenn Sie sozusagen alle Ihre Enten in einer Reihe halten und Hunde und Enten nicht miteinander vermischen, es sei denn, Sie behandeln sie als generische Tiere. Mit anderen Worten: Auch wenn die Schnittstelle fließend ist, ist sie immer noch da; es ist oft ein Fehler, einen Hund an einen Code zu übergeben, der von ihm erwartet, dass er quakt und fliegt.
Wenn Sie aber sicher sind, dass Sie das Richtige tun, können Sie das Quacking-Dog-Problem umgehen, indem Sie die Existenz einer bestimmten Methode testen, bevor Sie versuchen, sie anzuwenden. Etwas wie
if (typeof(someObject.quack) == "function")
{
// This thing can quack
}
So können Sie alle Methoden, die Sie verwenden können, überprüfen, bevor Sie sie einsetzen. Die Syntax ist allerdings etwas hässlich. Es gibt einen etwas hübscheren Weg:
Object.prototype.can = function(methodName)
{
return ((typeof this[methodName]) == "function");
};
if (someObject.can("quack"))
{
someObject.quack();
}
Es handelt sich um Standard-JavaScript, so dass es in jedem brauchbaren JS-Interpreter funktionieren sollte. Es hat den zusätzlichen Vorteil, dass es sich wie Englisch liest.
Bei modernen Browsern (d. h. bei so ziemlich jedem Browser außer IE 6-8) gibt es sogar eine Möglichkeit, die Eigenschaft nicht in for...in
:
Object.defineProperty(Object.prototype, 'can', {
enumerable: false,
value: function(method) {
return (typeof this[method] === 'function');
}
}
Das Problem ist, dass IE7-Objekte nicht über .defineProperty
überhaupt nicht, und im IE8 funktioniert es angeblich nur bei Host-Objekten (d. h. DOM-Elementen und dergleichen). Wenn Kompatibilität ein Thema ist, können Sie nicht verwenden .defineProperty
. (Ich werde den IE6 nicht einmal erwähnen, da er außerhalb Chinas kaum noch eine Rolle spielt).
Ein weiteres Problem ist, dass einige Kodierungsstile davon ausgehen, dass jeder schlechten Code schreibt, und die Änderung von Object.prototype
für den Fall, dass jemand blindlings die for...in
. Wenn Ihnen das wichtig ist, oder Sie (IMO gebrochen ) Code, der dies tut, versuchen Sie eine etwas andere Version:
function can(obj, methodName)
{
return ((typeof obj[methodName]) == "function");
}
if (can(someObject, "quack"))
{
someObject.quack();
}
Holen Sie sich ein Exemplar von ' JavaScript-Entwurfsmuster ' von Dustin Diaz . Es gibt ein paar Kapitel, die der Implementierung von JavaScript-Schnittstellen durch Duck Typing gewidmet sind. Auch das ist eine schöne Lektüre. Aber nein, es gibt keine spracheigene Implementierung einer Schnittstelle, man muss Ente Typ .
// 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 (ECMAScript Ausgabe 3) hat eine implements
reserviertes Wort aufgespart für zukünftige Verwendung . Ich denke, dass dies genau für diesen Zweck gedacht ist, aber in der Eile, die Spezifikation herauszubringen, hatten sie keine Zeit zu definieren, was damit zu tun ist, also tun die Browser derzeit nichts anderes, als es dort zu lassen und sich gelegentlich zu beschweren, wenn man versucht, es für etwas zu verwenden.
Es ist möglich und in der Tat einfach genug, eine eigene Object.implement(Interface)
Methode mit einer Logik, die sich wehrt, wenn ein bestimmter Satz von Eigenschaften/Funktionen in einem bestimmten Objekt nicht implementiert ist.
Ich habe einen Artikel geschrieben über Objektorientierung wobei ich meine eigene Notation wie folgt verwende :
// 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);
Es gibt viele Möglichkeiten, diese spezielle Katze zu häuten, aber dies ist die Logik, die ich für meine eigene Implementierung der Schnittstelle verwendet habe. Ich bevorzuge diesen Ansatz, und er ist einfach zu lesen und zu verwenden (wie Sie oben sehen können). Es bedeutet das Hinzufügen einer 'implement' Methode zu Function.prototype
Manche Leute haben damit vielleicht ein Problem, aber ich finde, es funktioniert wunderbar.
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;
}
Auch wenn JavaScript pas haben die interface
Typs ist sie oft erforderlich. Aus Gründen, die mit der dynamischen Natur von JavaScript und der Verwendung von Prototypical-Inheritance zusammenhängen, ist es schwierig, klassenübergreifend konsistente Schnittstellen zu gewährleisten - es ist jedoch möglich, dies zu tun, und wird häufig emuliert.
An diesem Punkt gibt es eine Handvoll bestimmter Wege, um Schnittstellen in JavaScript zu emulieren; die Varianz der Ansätze befriedigt in der Regel einige Bedürfnisse, während andere unbehandelt bleiben. Oftmals ist der robusteste Ansatz übermäßig schwerfällig und behindert den Implementierer (Entwickler).
Hier ist ein Ansatz für Schnittstellen / Abstrakte Klassen, der nicht sehr schwerfällig ist, explizit ist, Implementierungen innerhalb von Abstraktionen auf ein Minimum beschränkt und genügend Raum für dynamische oder benutzerdefinierte Methoden lässt:
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
};
Vorschrift Auflöser
En resolvePrecept
Funktion ist ein Dienstprogramm und eine Hilfsfunktion zur Verwendung innerhalb Ihrer Abstrakte Klasse . Seine Aufgabe ist es, eine angepasste Handhabung der gekapselten Implementierung zu ermöglichen Grundsätze (Daten und Verhalten) . Er kann Fehler auslösen oder Warnungen ausgeben UND der Implementor-Klasse einen Standardwert zuweisen.
iAbstractClass
En iAbstractClass
definiert die zu verwendende Schnittstelle. Ihr Ansatz beinhaltet eine stillschweigende Vereinbarung mit ihrer Implementor-Klasse. Diese Schnittstelle ordnet jedem Vorschrift in genau denselben Vorgabe-Namensraum -- ODER -- in denjenigen, den die Vorschrift Auflöser Funktion zurück. Die stillschweigende Übereinkunft führt jedoch zu einer Kontext -- eine Bestimmung des Implementors.
Durchführer
Der Implementor 'stimmt' einfach mit einer Schnittstelle überein ( iAbstractClass in diesem Fall) und wendet es durch die Verwendung von Konstrukteur-Hijacking : iAbstractClass.apply(this)
. Indem Sie die Daten und das Verhalten oben definieren und dann Entführung des Konstruktors der Schnittstelle -- Übergabe des Implementor-Kontextes an den Konstruktor der Schnittstelle -- können wir sicherstellen, dass die Überschreibungen des Implementors hinzugefügt werden und dass die Schnittstelle Warnungen und Standardwerte angibt.
Dies ist ein sehr unkomplizierter Ansatz, der meinem Team und mir im Laufe der Zeit und bei verschiedenen Projekten sehr gute Dienste geleistet hat. Allerdings hat es einige Vorbehalte und Nachteile.
Beeinträchtigungen
Dies trägt zwar in erheblichem Maße dazu bei, die Konsistenz Ihrer Software zu gewährleisten, aber es keine echten Schnittstellen implementiert -- sondern ahmt sie nach. Obwohl Definitionen, Standardwerte und Warnungen oder Fehler son expliziert wird, ist die Explikation der Nutzung durchgesetzt & behauptet durch den Entwickler (wie bei einem Großteil der JavaScript-Entwicklung).
Dies scheint der beste Ansatz zu sein, um "Schnittstellen in JavaScript" Ich würde es jedoch begrüßen, wenn die folgenden Fragen gelöst würden:
delete
AktionenIn diesem Sinne hoffe ich, dass diese Informationen Ihnen genauso viel helfen wie meinem Team und mir.
Abstrakte Schnittstelle wie diese
const MyInterface = {
serialize: () => {throw "must implement serialize for MyInterface types"},
print: () => console.log(this.serialize())
}
eine Instanz erstellen:
function MyType() {
this.serialize = () => "serialized "
}
MyType.prototype = MyInterface
und verwenden Sie es
let x = new MyType()
x.print()
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.