J'apprends comment faire OOP avec JavaScript . At-il le concept d'interface (tel que interface
de Java)?
Donc, je serais capable de créer un auditeur ...
Il n'y a pas de notion de "cette classe doit avoir ces fonctions" (c'est-à-dire, aucune interface en soi), parce que:
Au lieu de cela, JavaScript utilise ce qui s'appelle dactylographie . (S'il marche comme un canard et que les charlatans ressemblent à un canard, autant que JS le soucie, c'est un canard.) Si votre objet a les méthodes quack (), walk () et fly (), le code peut l'utiliser où il le souhaite. un objet qui peut marcher, charmer et voler, sans nécessiter la mise en œuvre d'une interface "Duckable". L'interface est exactement l'ensemble des fonctions que le code utilise (et les valeurs de retour de ces fonctions), et avec la saisie simplifiée, vous l'obtenez gratuitement.
Cela ne veut pas dire que votre code n'échouera pas à la moitié si vous essayez d'appeler some_dog.quack()
; vous obtiendrez un TypeError. Franchement, si vous dites aux chiens de charrier, vous avez des problèmes un peu plus gros; La dactylographie fonctionne mieux lorsque vous maintenez tous vos canards dans une rangée, pour ainsi dire, et que vous ne laissez pas les chiens et les canards se mélanger à moins que vous ne les traitiez comme des animaux génériques. En d'autres termes, même si l'interface est fluide, elle est toujours là. c'est souvent une erreur de passer un chien à un code qui s'attend à ce qu'il charrie et vole en premier.
Mais si vous êtes certain de faire le bon choix, vous pouvez contourner le problème des charlatans en testant l'existence d'une méthode particulière avant d'essayer de l'utiliser. Quelque chose comme
if (typeof(someObject.quack) == "function")
{
// This thing can quack
}
Ainsi, vous pouvez vérifier toutes les méthodes que vous pouvez utiliser avant de les utiliser. La syntaxe est un peu moche, cependant. Il y a une manière un peu plus jolie:
Object.prototype.can = function(methodName)
{
return ((typeof this[methodName]) == "function");
};
if (someObject.can("quack"))
{
someObject.quack();
}
Ceci est du code JavaScript standard, il devrait donc fonctionner dans tout interpréteur JS intéressant. Il présente l'avantage supplémentaire de lire comme l'anglais.
Pour les navigateurs modernes (c'est-à-dire pratiquement n'importe quel navigateur autre que IE 6-8), il existe même un moyen d'empêcher l'affichage de la propriété dans for...in
:
Object.defineProperty(Object.prototype, 'can', {
enumerable: false,
value: function(method) {
return (typeof this[method] === 'function');
}
}
Le problème est que les objets IE7 n'ont pas du tout .defineProperty
et que dans IE8, cela ne fonctionnerait que sur les objets Host (c'est-à-dire les éléments DOM et autres). Si la compatibilité pose problème, vous ne pouvez pas utiliser .defineProperty
. (Je ne mentionnerai même pas IE6, car ce n'est plus vraiment pertinent en dehors de la Chine.)
Un autre problème est que certains styles de codage aiment supposer que tout le monde écrit un code incorrect et interdisent de modifier Object.prototype
au cas où quelqu'un voudrait utiliser aveuglément for...in
. Si cela vous intéresse ou si vous utilisez un code (IMO cassé ), essayez une version légèrement différente:
function can(obj, methodName)
{
return ((typeof obj[methodName]) == "function");
}
if (can(someObject, "quack"))
{
someObject.quack();
}
Procurez-vous une copie de ' patterns de conception JavaScript ' par Dustin Diaz . Quelques chapitres sont consacrés à la mise en oeuvre d'interfaces JavaScript via Duck Typing. C'est une bonne lecture aussi. Mais non, il n'y a pas d'implémentation native en langage d'une interface, vous devez Type de canard .
// 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 (édition ECMAScript 3) a un implements
mot réservé sauvegardé pour une utilisation future . Je pense que cela est exactement prévu à cet effet, cependant, dans le souci pressant de faire passer le cahier des charges, ils n’ont pas eu le temps de définir quoi en faire, de sorte que, pour le moment, les navigateurs ne font rien d'autre que laissez-le reposer là-bas et de temps en temps vous plaindre si vous essayez de l'utiliser pour quelque chose.
Il est possible et assez simple de créer votre propre méthode Object.implement(Interface)
avec une logique qui bloque chaque fois qu'un ensemble particulier de propriétés/fonctions n'est pas implémenté dans un objet donné.
J'ai écrit un article sur orientation objetoù utiliser ma propre notation comme suit:
// 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);
Il y a beaucoup de façons de skinner ce chat en particulier, mais c'est la logique que j'ai utilisée pour ma propre implémentation d'interface. Je trouve que je préfère cette approche, et elle est facile à lire et à utiliser (comme vous pouvez le voir ci-dessus). Cela veut dire ajouter une méthode 'implémenter' à Function.prototype
avec laquelle certaines personnes peuvent avoir un problème, mais je trouve que cela fonctionne à merveille.
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;
}
Bien que JavaScript n'ait pas le type interface
, il est souvent nécessaire. Pour des raisons liées à la nature dynamique de JavaScript et à l'utilisation de l'héritage prototypical, il est difficile de garantir la cohérence des interfaces entre les classes. Toutefois, il est possible de le faire. et fréquemment imité.
À ce stade, il existe une poignée de méthodes particulières pour émuler les interfaces en JavaScript; la variance des approches répond généralement à certains besoins, tandis que d’autres ne sont pas traitées. Souvent, l'approche la plus robuste est trop lourde et bloque l'implémenteur (développeur).
Voici une approche des classes d'interfaces/abstraites peu lourde, explicative, minimisant les implémentations dans les Abstractions et laissant suffisamment de place aux méthodologies dynamiques ou personnalisées:
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
};
résolveur de préceptes
La fonction resolvePrecept
est un utilitaire et une fonction d'assistance à utiliser dans votre classe abstraite . Son travail consiste à permettre la gestion personnalisée de la mise en œuvre des préceptes (données et comportements) encapsulés . Il peut renvoyer des erreurs ou avertir - ET - d'attribuer une valeur par défaut à la classe Implementor.
iAbstractClass
Le iAbstractClass
définit l'interface à utiliser. Son approche implique un accord tacite avec sa classe de mise en œuvre. Cette interface assigne chaque précepte au même espace de nom de précepte exact - OR - à quelque soit le La fonction de précepteur résolveur est renvoyée. Cependant, l'accord tacite résout un contexte - une disposition de Implementor.
Exécuteur
L’implémenteur "accepte" simplement une interface ( iAbstractClass dans ce cas) et l’applique à l’aide de Constructor - Détournement : iAbstractClass.apply(this)
. En définissant les données et le comportement ci-dessus, puis piratant le constructeur de l'interface - en transmettant le contexte de l'implémenteur au constructeur de l'interface - nous pouvons nous assurer que les remplacements de l'implémenteur être ajouté et cette interface expliquera les avertissements et les valeurs par défaut.
C'est une approche très peu lourde qui a très bien servi mon équipe et moi-même au fil du temps et des différents projets. Cependant, il y a quelques réserves et inconvénients.
Inconvénients
Bien que cela aide à mettre en œuvre la cohérence de votre logiciel dans une mesure significative, ne met pas en oeuvre de véritables interfaces - mais les émule. Bien que les définitions, les valeurs par défaut et les avertissements ou erreurs soient expliqués , l'explication d'utilisation est appliquée et affirmée par le développeur (comme beaucoup de développement JavaScript).
C'est apparemment la meilleure approche de "Interfaces en JavaScript" , cependant, j'aimerais beaucoup que ce qui suit soit résolu:
delete
Cela dit, j'espère que cela vous aidera autant que moi et mon équipe.
J'espère que tous ceux qui recherchent encore une réponse trouvent cela utile.
Vous pouvez essayer d’utiliser un proxy (standard depuis 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;
}
});
Ensuite, vous pouvez facilement dire:
myMap = {}
myMap.position = latLngLiteral;
Vous avez besoin d’interfaces dans Java car il est typé de manière statique et le contrat entre les classes doit être connu lors de la compilation. En JavaScript c'est différent. JavaScript est typé dynamiquement. cela signifie que lorsque vous obtenez l'objet, vous pouvez simplement vérifier s'il a une méthode spécifique et l'appeler.
Lorsque vous souhaitez utiliser un transcompilateur, vous pouvez essayer TypeScript. Il prend en charge les projets de fonctionnalités ECMA similaires aux langages tels que coffeescript ou babel.
Dans TypeScript, votre interface peut ressembler à:
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"
}
Ce que tu ne peux pas faire:
Javascript n'a pas d'interfaces. Mais il peut être typé, un exemple peut être trouvé ici:
http://reinsbrain.blogspot.com/2008/10/interface-in-javascript.html
il n'y a pas d'interface native en JavaScript, il existe plusieurs façons de simuler une interface. j'ai écrit un paquet qui le fait
vous pouvez voir l'implantation ici
Je sais que c’est un ancien système, mais j’ai récemment constaté qu’il me fallait de plus en plus disposer d’une API pratique pour vérifier les objets par rapport aux interfaces. J'ai donc écrit ceci: https://github.com/tomhicks/methodical
Il est également disponible via NPM: npm install methodical
Il fait essentiellement tout ce qui est suggéré ci-dessus, avec quelques options pour être un peu plus strict, et tout cela sans avoir à faire des charges de if (typeof x.method === 'function')
.
Espérons que quelqu'un le trouve utile.