JavaScript ha il tipo di interfaccia (come “l’interfaccia” di Java)?

Sto imparando come creare OOP con JavaScript . Ha il concetto di interfaccia (come l’ interface di Java)?

Quindi sarei in grado di creare un ascoltatore …

Non c’è idea di “questa class deve avere queste funzioni” (cioè, nessuna interfaccia di per sé), perché:

  1. L’ereditarietà di JavaScript si basa sugli oggetti, non sulle classi. Non è un grosso problema finché non ti rendi conto:
  2. JavaScript è un linguaggio tipizzato in modo estremamente dinamico: puoi creare un object con i metodi appropriati, che lo renderebbero conforms all’interfaccia e quindi indefinire tutte le cose che lo rendevano conforms . Sarebbe così facile sovvertire il sistema di tipi, anche accidentalmente! – che non varrebbe la pena provare e creare un sistema di tipi in primo luogo.

Invece, JavaScript usa quella che viene chiamata digitazione anatra . (Se cammina come un’anatra e caga come un’anatra, per quanto riguarda JS, è un’anatra.) Se il tuo object ha metodi quack (), walk () e fly (), il codice può usarlo ovunque si aspetti un object che può camminare, ciarlare e volare, senza richiedere l’implementazione di qualche interfaccia “Duckable”. L’interfaccia è esattamente l’insieme di funzioni che utilizza il codice (e i valori di ritorno da quelle funzioni), e con la digitazione anatra, lo si ottiene gratuitamente.

Ora, questo non vuol dire che il tuo codice non fallirà a metà strada, se provi a chiamare some_dog.quack() ; otterrai un errore TypeError. Francamente, se stai dicendo ai cani di ciarlare, hai problemi leggermente più grandi; La digitazione anatra funziona meglio quando si tengono tutte le anatre di fila, per così dire, e non si lasciano che i cani e le anatre si uniscano insieme a meno che non li si tratti come animali generici. In altre parole, anche se l’interfaccia è fluida, è ancora lì; spesso è un errore passare un cane al codice che si aspetta che metta fine al volo e voli in primo luogo.

Ma se sei sicuro di fare la cosa giusta, puoi risolvere il problema del ciarlatano testando l’esistenza di un particolare metodo prima di provare ad usarlo. Qualcosa di simile a

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

Quindi puoi verificare tutti i metodi che puoi usare prima di usarli. La syntax è piuttosto brutta, però. C’è un modo leggermente più carino:

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

Questo è JavaScript standard, quindi dovrebbe funzionare in qualsiasi interprete JS che valga la pena di utilizzare. Ha il vantaggio di leggere come l’inglese.

Per i browser moderni (ovvero praticamente tutti i browser diversi da IE 6-8), c’è anche un modo per non mostrare la proprietà in for...in :

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

Il problema è che gli oggetti IE7 non hanno affatto .defineProperty e in IE8, presumibilmente, funziona solo sugli oggetti host (ovvero, elementi DOM e così via). Se la compatibilità è un problema, non è ansible utilizzare .defineProperty . (Non parlerò nemmeno di IE6, perché è alquanto irrilevante al di fuori della Cina.)

Un altro problema è che alcuni stili di codice amano supporre che tutti scrivano codice Object.prototype e proibiscano di modificare Object.prototype nel caso in cui qualcuno voglia usare ciecamente for...in . Se ti interessa, o stai usando (codice IMO rotto ), prova una versione leggermente diversa:

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

Prendi una copia di ” JavaScript design patterns ” di Dustin Diaz . Ci sono alcuni capitoli dedicati all’implementazione di interfacce JavaScript tramite Duck Typing. È anche una bella lettura. Ma no, non esiste un’implementazione nativa di un’interfaccia, devi digitare 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 } 

JavaScript (ECMAScript edition 3) ha una parola riservata agli implements salvata per un uso futuro . Penso che questo sia inteso esattamente per questo scopo, tuttavia, in fretta per ottenere le specifiche dalla porta, non hanno avuto il tempo di definire cosa farne, quindi, al momento, i browser non fanno altro lascia che si sieda lì e di tanto in tanto si lamenti se cerchi di usarlo per qualcosa.

È ansible ed è anche abbastanza facile creare il proprio Object.implement(Interface) con la logica che blocca ogni volta che un particolare insieme di proprietà / funzioni non è implementato in un dato object.

Ho scritto un articolo sull’orientamento agli oggetti in cui usare la mia notazione come segue :

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

Ci sono molti modi per skinare questo particolare gatto, ma questa è la logica che ho usato per la mia implementazione dell’interfaccia. Trovo che preferisco questo approccio, ed è facile da leggere e utilizzare (come puoi vedere sopra). Significa aggiungere un metodo ‘implement’ a Function.prototype che alcune persone potrebbero avere un problema con, ma trovo che funzioni magnificamente.

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

Interfacce JavaScript:

Sebbene JavaScript non abbia il tipo di interface , è spesso necessario. Per ragioni relative alla natura dynamic di JavaScript e all’utilizzo di Prototypical-Inheritance, è difficile garantire interfacce coerenti tra le classi – tuttavia, è ansible farlo; e frequentemente emulato.

A questo punto, esistono alcuni modi particolari per emulare le interfacce in JavaScript; la varianza sugli approcci di solito soddisfa alcuni bisogni, mentre altri non vengono affrontati. Spesso, l’approccio più robusto è eccessivamente ingombrante e ostacola l’implementatore (sviluppatore).

Ecco un approccio a Interfaces / Abstract Classes che non è molto complicato, è esplicativo, mantiene le implementazioni all’interno di Abstractions al minimo e lascia abbastanza spazio per le metodologie dinamiche o personalizzate:

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

I partecipanti

Precept Resolver

La funzione resolvePrecept è una funzione di utilità e aiuto da utilizzare all’interno della class astratta . Il suo compito è quello di consentire la gestione personalizzata dell’implementazione dei precetti incapsulati (dati e comportamento) . Può generare errori o warn – AND – assegnare un valore predefinito alla class Implementor.

iAbstractClass

iAbstractClass definisce l’interfaccia da utilizzare. Il suo approccio implica un tacito accordo con la sua class Implementor. Questa interfaccia assegna ciascun precetto allo stesso spazio dei nomi precetto esatto – OR – a qualunque funzione restituisca la funzione Precept Resolver . Tuttavia, il tacito accordo si risolve in un contesto : una disposizione di Implementor.

implementor

L’Implementatore semplicemente “concorda” con un’interfaccia (in questo caso iAbstractClass ) e lo applica mediante l’uso di Constructor-Hijacking : iAbstractClass.apply(this) . Definendo i dati e il comportamento di cui sopra, e poi dirottando il costruttore dell’interfaccia, passando il contesto di Implementor al costruttore di Interface, possiamo garantire che le sostituzioni di Implementor verranno aggiunte e che Interfaccia esplicherà avvisi e valori predefiniti.

Questo è un approccio molto non ingombrante che ha servito molto bene il mio team e io per il corso del tempo e diversi progetti. Tuttavia, ha alcuni avvertimenti e svantaggi.

svantaggi

Sebbene questo aiuti ad implementare la coerenza nel software in misura significativa, non implementa interfacce vere, ma le emula. Anche se vengono esplicate definizioni, valori predefiniti e avvertimenti o errori, la spiegazione dell’uso viene applicata e asserita dallo sviluppatore (come con gran parte dello sviluppo di JavaScript).

Questo è apparentemente il miglior approccio a “Interfacce in JavaScript” , tuttavia mi piacerebbe vedere risolto quanto segue:

  • Asserzioni sui tipi di ritorno
  • Asserzioni di firme
  • Blocca oggetti dalle azioni di delete
  • Asserzioni di qualsiasi altra cosa prevalente o necessaria nella specificità della comunità JavaScript

Detto questo, spero che questo ti aiuti tanto quanto ha il mio team e I.

Spero che chiunque stia ancora cercando una risposta lo trovi utile.

Puoi provare a usare un Proxy (è standard dal momento in cui 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; } }); 

Quindi puoi facilmente dire:

 myMap = {} myMap.position = latLngLiteral; 

Hai bisogno di interfacce in Java poiché è tipizzato staticamente e il contratto tra le classi dovrebbe essere conosciuto durante la compilazione. In JavaScript è diverso. JavaScript è tipizzato dynamicmente; significa che quando ottieni l’object puoi semplicemente controllare se ha un metodo specifico e chiamarlo.

So che questo è vecchio, ma di recente mi sono ritrovato a dover sempre più avere una comoda API per il controllo degli oggetti rispetto alle interfacce. Così ho scritto questo: https://github.com/tomhicks/methodical

È anche disponibile tramite NPM: npm install methodical

Fondamentalmente fa tutto quanto suggerito sopra, con alcune opzioni per essere un po ‘più rigidi, e tutto senza dover fare un sacco di if (typeof x.method === 'function') boilerplate.

Spero che qualcuno lo trovi utile.

Javascript non ha interfacce. Ma può essere digitato anatra, un esempio può essere trovato qui:

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

C’è anche jQuery.isFunction (metodo), se preferisci che il codice di cHao.

Quando vuoi usare un transcompiler, puoi provare TypeScript. Supporta bozze di funzioni ECMA simili a quelle che fanno le lingue come coffeescript o babel.

In TypeScript la tua interfaccia può essere simile a:

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

Cosa non puoi fare:

  • Definisci i modelli RegExp per il valore del tipo
  • Definire la convalida come la lunghezza della stringa
  • Intervalli numerici
  • eccetera.