web-dev-qa-db-fra.com

Comment "correctement" créer un objet personnalisé en JavaScript?

Je me demande quel est le meilleur moyen de créer un objet JavaScript doté de propriétés et de méthodes.

J'ai vu des exemples où la personne a utilisé var self = this et ensuite utilisé self. dans toutes les fonctions pour s'assurer que la portée est toujours correcte.

Ensuite, j'ai vu des exemples d'utilisation de .prototype pour ajouter des propriétés, tandis que d'autres le font en ligne.

Quelqu'un peut-il me donner un exemple approprié d'objet JavaScript avec certaines propriétés et méthodes?

457
Michael Stum

Il existe deux modèles d'implémentation de classes et d'instances en JavaScript: la méthode de prototypage et la méthode de fermeture. Les deux ont des avantages et des inconvénients, et il existe de nombreuses variantes étendues. De nombreux programmeurs et bibliothèques ont des approches et des fonctions d’utilitaire de traitement de classe différentes qui permettent d’appliquer certaines des parties les plus laides du langage.

Le résultat est que dans une société mixte, vous aurez un méli-mélo de métaclasses, se comportant tous légèrement différemment. Pire encore, la plupart des didacticiels JavaScript sont terribles et offrent une sorte de compromis intermédiaire pour couvrir toutes les bases, ce qui vous laisse très perplexe. (Probablement l'auteur est également confus. Le modèle d'objet de JavaScript est très différent de la plupart des langages de programmation, et souvent mal conçu.)

Commençons par à l’aide du prototype . C'est le plus natif de JavaScript que vous puissiez obtenir: il y a un minimum de code overhead et instanceof fonctionnera avec des instances de ce type d'objet.

function Shape(x, y) {
    this.x= x;
    this.y= y;
}

Nous pouvons ajouter des méthodes à l'instance créée par new Shape en les écrivant dans la recherche prototype de cette fonction constructeur:

Shape.prototype.toString= function() {
    return 'Shape at '+this.x+', '+this.y;
};

Maintenant, pour le sous-classer, dans la mesure où vous pouvez appeler ce que JavaScript fait sous-classer. Nous faisons cela en remplaçant complètement cette étrange propriété prototype magique:

function Circle(x, y, r) {
    Shape.call(this, x, y); // invoke the base class's constructor function to take co-ords
    this.r= r;
}
Circle.prototype= new Shape();

avant d'ajouter des méthodes:

Circle.prototype.toString= function() {
    return 'Circular '+Shape.prototype.toString.call(this)+' with radius '+this.r;
}

Cet exemple fonctionnera et vous verrez le code semblable dans de nombreux tutoriels. Mais mec, ce new Shape() est moche: nous instancions la classe de base même si aucune forme réelle ne doit être créée. Cela fonctionne dans ce cas simple, car JavaScript est tellement bâclé: il ne permet de passer aucun argument, auquel cas x et y deviennent undefined et sont affectés aux prototypes this.x et this.y du prototype. Si la fonction constructeur faisait quelque chose de plus compliqué, elle tomberait à plat face.

Nous devons donc trouver un moyen de créer un objet prototype contenant les méthodes et les autres membres souhaités au niveau de la classe, sans appeler la fonction constructeur de la classe de base. Pour ce faire, nous allons devoir commencer à écrire du code d'assistance. C'est l'approche la plus simple que je connaisse:

function subclassOf(base) {
    _subclassOf.prototype= base.prototype;
    return new _subclassOf();
}
function _subclassOf() {};

Cela transfère les membres de la classe de base de son prototype à une nouvelle fonction constructeur qui ne fait rien, puis utilise ce constructeur. Maintenant nous pouvons écrire simplement:

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.prototype= subclassOf(Shape);

à la place de new Shape() Wright. Nous avons maintenant un ensemble acceptable de primitives aux classes construites.

Nous pouvons envisager quelques améliorations et extensions dans le cadre de ce modèle. Par exemple, voici une version syntactical-sugar:

Function.prototype.subclass= function(base) {
    var c= Function.prototype.subclass.nonconstructor;
    c.prototype= base.prototype;
    this.prototype= new c();
};
Function.prototype.subclass.nonconstructor= function() {};

...

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.subclass(Shape);

L'une ou l'autre version présente l'inconvénient que la fonction constructeur ne peut pas être héritée, comme c'est le cas dans de nombreux langages. Ainsi, même si votre sous-classe n’ajoute rien au processus de construction, vous devez vous rappeler d’appeler le constructeur de base avec les arguments que la base souhaite. Ceci peut être légèrement automatisé avec apply, mais vous devez quand même écrire:

function Point() {
    Shape.apply(this, arguments);
}
Point.subclass(Shape);

Une extension commune consiste donc à décomposer le processus d'initialisation dans sa propre fonction plutôt que dans le constructeur lui-même. Cette fonction peut alors hériter de la base parfaitement:

function Shape() { this._init.apply(this, arguments); }
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

function Point() { this._init.apply(this, arguments); }
Point.subclass(Shape);
// no need to write new initialiser for Point!

Maintenant, nous venons de recevoir le même passe-partout pour chaque classe. Peut-être pourrions-nous transférer cela dans sa propre fonction d'assistance afin d'éviter de le taper, par exemple au lieu de Function.prototype.subclass, en le retournant et en laissant la fonction Fonction de la classe de base cracher des sous-classes:

Function.prototype.makeSubclass= function() {
    function Class() {
        if ('_init' in this)
            this._init.apply(this, arguments);
    }
    Function.prototype.makeSubclass.nonconstructor.prototype= this.prototype;
    Class.prototype= new Function.prototype.makeSubclass.nonconstructor();
    return Class;
};
Function.prototype.makeSubclass.nonconstructor= function() {};

...

Shape= Object.makeSubclass();
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

Point= Shape.makeSubclass();

Circle= Shape.makeSubclass();
Circle.prototype._init= function(x, y, r) {
    Shape.prototype._init.call(this, x, y);
    this.r= r;
};

... qui commence à ressembler un peu plus aux autres langues, bien qu'avec une syntaxe légèrement plus lourde. Vous pouvez ajouter quelques fonctionnalités supplémentaires si vous le souhaitez. Peut-être voudriez-vous que makeSubclass prenne et retienne un nom de classe et fournisse une toString par défaut l’utilisant. Peut-être souhaitez-vous que le constructeur détecte qu'il a été appelé par inadvertance sans l'opérateur new (sinon, le débogage serait très gênant):

Function.prototype.makeSubclass= function() {
    function Class() {
        if (!(this instanceof Class))
            throw('Constructor called without "new"');
        ...

Peut-être voudrez-vous transmettre tous les nouveaux membres et que makeSubclass les ajoute au prototype, pour vous éviter de devoir écrire Class.prototype... tout à fait. Beaucoup de systèmes de classes le font, par exemple:

Circle= Shape.makeSubclass({
    _init: function(x, y, z) {
        Shape.prototype._init.call(this, x, y);
        this.r= r;
    },
    ...
});

Il existe de nombreuses fonctionnalités potentielles que vous pourriez considérer souhaitables dans un système d'objet et personne ne s'accorde vraiment sur une formule en particulier.


La manière fermeture , alors. Cela évite les problèmes d'héritage basé sur un prototype de JavaScript, en n'utilisant pas l'héritage du tout. Au lieu:

function Shape(x, y) {
    var that= this;

    this.x= x;
    this.y= y;

    this.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };
}

function Circle(x, y, r) {
    var that= this;

    Shape.call(this, x, y);
    this.r= r;

    var _baseToString= this.toString;
    this.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+that.r;
    };
};

var mycircle= new Circle();

Désormais, chaque instance de Shape aura sa propre copie de la méthode toString (et toutes les autres méthodes ou autres membres de la classe que nous ajoutons).

La mauvaise chose à propos de chaque instance ayant sa propre copie de chaque membre du groupe, c'est que c'est moins efficace. Si vous avez affaire à un grand nombre d'instances sous-classées, l'héritage prototype peut mieux vous servir. Aussi, appeler une méthode de la classe de base est légèrement gênant, comme vous pouvez le constater: nous devons nous rappeler quelle était la méthode avant que le constructeur de la sous-classe ne l'ait écrasée, sinon elle serait perdue.[De plus, comme il n'y a pas d'héritage ici, l'opérateur instanceof ne fonctionnera pas; vous devrez fournir votre propre mécanisme de détection de classe si vous en avez besoin. Tandis que vous pouvez manipuler les objets prototypes de la même manière que pour l’héritage des prototypes, c’est un peu délicat et ne vaut pas la peine de le faire simplement pour faire fonctionner instanceof.].

La bonne chose à propos de chaque instance ayant sa propre méthode est que la méthode peut ensuite être liée à l'instance spécifique à laquelle elle appartient. Ceci est utile en raison de la façon bizarre de lier this dans les appels de méthode de JavaScript, ce qui a pour résultat que si vous détachez une méthode de son propriétaire:

var ts= mycircle.toString; alert(ts());

avec le prototype, vous devez inclure une clôture pour chaque mission:.

setTimeout(function() { mycircle.move(1, 1); }, 1000);

setTimeout(mycircle.move.bind(mycircle, 1, 1), 1000);

il y a beaucoup de variantes sur la méthode de fermeture aussi. Vous préférerez peut-être omettre complètement this, créer une nouvelle that et la renvoyer au lieu d'utiliser l'opérateur new:.

function Shape(x, y) { var that= {}; that.x= x; that.y= y; that.toString= function() { return 'Shape at '+that.x+', '+that.y; }; return that; } function Circle(x, y, r) { var that= Shape(x, y); that.r= r; var _baseToString= that.toString; that.toString= function() { return 'Circular '+_baseToString(that)+' with radius '+r; }; return that; }; var mycircle= Circle(); // you can include `new` if you want but it won't do anything

Mais les deux manières sont assez contre-intuitives pour la plupart des programmeurs. Les deux ont beaucoup de variations désordonnées potentielles. Vous rencontrerez les deux (ainsi que de nombreux schémas intermédiaires et généralement brisés) si vous utilisez le code/les bibliothèques d'autres personnes. Il n'y a pas de réponse généralement acceptée. Bienvenue dans le monde merveilleux des objets JavaScript.

[Cela fait partie 94 de Pourquoi JavaScript n'est pas mon langage de programmation préféré.].

[This has been part 94 of Why JavaScript Is Not My Favourite Programming Language.]

866
bobince

J'utilise ce modèle assez fréquemment - j'ai constaté qu'il me donnait une assez grande souplesse lorsque j'en ai besoin. En utilisation, il est assez similaire aux classes de style Java.

var Foo = function()
{

    var privateStaticMethod = function() {};
    var privateStaticVariable = "foo";

    var constructor = function Foo(foo, bar)
    {
        var privateMethod = function() {};
        this.publicMethod = function() {};
    };

    constructor.publicStaticMethod = function() {};

    return constructor;
}();

Ceci utilise une fonction anonyme appelée à la création, renvoyant une nouvelle fonction constructeur. La fonction anonyme n'étant appelée qu'une seule fois, vous pouvez y créer des variables statiques privées (elles se trouvent dans la fermeture, visibles pour les autres membres de la classe). La fonction constructeur est essentiellement un objet Javascript standard: vous définissez des attributs privés à l'intérieur de celui-ci, et des attributs publics sont attachés à la variable this.

Fondamentalement, cette approche combine l'approche de Crockfordian avec des objets Javascript standard pour créer une classe plus puissante.

Vous pouvez l'utiliser comme n'importe quel autre objet Javascript:

Foo.publicStaticMethod(); //calling a static method
var test = new Foo();     //instantiation
test.publicMethod();      //calling a method
90
ShZ

Douglas Crockford discute abondamment de ce sujet dans The Good Parts. Il recommande d'éviter à l'opérateur new de créer de nouveaux objets. Au lieu de cela, il propose de créer des constructeurs personnalisés. Par exemple:

var mammal = function (spec) {     
   var that = {}; 
   that.get_name = function (  ) { 
      return spec.name; 
   }; 
   that.says = function (  ) { 
      return spec.saying || ''; 
   }; 
   return that; 
}; 

var myMammal = mammal({name: 'Herb'});

En Javascript, une fonction est un objet et peut être utilisée pour construire des objets avec l'opérateur new . Par convention, les fonctions destinées à être utilisées en tant que constructeurs commencent par une lettre majuscule. Vous voyez souvent des choses comme:

function Person() {
   this.name = "John";
   return this;
}

var person = new Person();
alert("name: " + person.name);**

Si vous oubliez d'utiliser l'opérateur new lors de l'instanciation d'un nouvel objet, vous obtenez un appel de fonction ordinaire et this est lié à l'objet global au lieu du nouvel objet.

24
Diego Pino

Continuer sur réponse de bobince

Dans es6, vous pouvez maintenant créer une variable class

Alors maintenant, vous pouvez faire:

class Shape {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    toString() {
        return `Shape at ${this.x}, ${this.y}`;
    }
}

Alors prolongez en cercle (comme dans l’autre réponse), vous pouvez faire:

class Circle extends Shape {
    constructor(x, y, r) {
        super(x, y);
        this.r = r;
    }

    toString() {
        let shapeString = super.toString();
        return `Circular ${shapeString} with radius ${this.r}`;
    }
}

Finit un peu plus propre en es6 et un peu plus facile à lire.


Voici un bon exemple de cela en action: 

class Shape {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return `Shape at ${this.x}, ${this.y}`;
  }
}

class Circle extends Shape {
  constructor(x, y, r) {
    super(x, y);
    this.r = r;
  }

  toString() {
    let shapeString = super.toString();
    return `Circular ${shapeString} with radius ${this.r}`;
  }
}

let c = new Circle(1, 2, 4);

console.log('' + c, c);

12
Neal

Vous pouvez également le faire de cette façon, en utilisant des structures:

function createCounter () {
    var count = 0;

    return {
        increaseBy: function(nb) {
            count += nb;
        },
        reset: function {
            count = 0;
        }
    }
}

Ensuite :

var counter1 = createCounter();
counter1.increaseBy(4);
6
Eino Gourdin

Quand on utilise le truc de fermer sur "ceci" lors d'une invocation de constructeur, c'est pour écrire une fonction qui peut être utilisée comme callback par un autre objet qui ne veut pas invoquer une méthode sur un objet. Ce n'est pas lié à "rendre la portée correcte".

Voici un objet JavaScript Vanilla:

function MyThing(aParam) {
    var myPrivateVariable = "squizzitch";

    this.someProperty = aParam;
    this.useMeAsACallback = function() {
        console.log("Look, I have access to " + myPrivateVariable + "!");
    }
}

// Every MyThing will get this method for free:
MyThing.prototype.someMethod = function() {
    console.log(this.someProperty);
};

Vous pourriez avoir beaucoup de mal à lire ce que Douglas Crockford a à dire à propos de JavaScript. John Resig est également brillant. Bonne chance!

4
Jonathan Feinberg

Une autre solution serait http://jsfiddle.net/nnUY4/ (Je ne sais pas si ce type de gestion de création d’objet et de fonction de révélation suit un modèle spécifique)

// Build-Reveal

var person={
create:function(_name){ // 'constructor'
                        //  prevents direct instantiation 
                        //  but no inheritance
    return (function() {

        var name=_name||"defaultname";  // private variable

        // [some private functions]

        function getName(){
            return name;
        }

        function setName(_name){
            name=_name;
        }

        return {    // revealed functions
            getName:getName,    
            setName:setName
        }
    })();
   }
  }

  // … no (instantiated) person so far …

  var p=person.create(); // name will be set to 'defaultname'
  p.setName("adam");        // and overwritten
  var p2=person.create("eva"); // or provide 'constructor parameters'
  alert(p.getName()+":"+p2.getName()); // alerts "adam:eva"
4
Fluchtpunkt

Closure est polyvalent. bobince a bien résumé les approches prototype vs fermeture lors de la création d'objets. Cependant, vous pouvez imiter certains aspects de OOP en utilisant la fermeture de manière fonctionnelle. Rappelez-vous les fonctions sont des objets en JavaScript} _; utilisez donc la fonction comme objet d’une manière différente.

Voici un exemple de fermeture:

function outer(outerArg) {
    return inner(innerArg) {
        return innerArg + outerArg; //the scope chain is composed of innerArg and outerArg from the outer context 
    }
}

Il y a quelque temps, je suis tombé sur l'article de Mozilla sur Closure. Voici ce qui saute aux yeux: "Une fermeture vous permet d'associer des données (l'environnement) à une fonction qui agit sur ces données. Cela présente des parallèles évidents avec la programmation orientée objet, où les objets nous permettent d'associer certaines données (les propriétés de l'objet) avec une ou plusieurs méthodes ". C’était la toute première fois que je lisais un parallélisme entre fermeture et classique OOP sans référence au prototype.

Comment?

Supposons que vous souhaitiez calculer la TVA de certains articles. La TVA est susceptible de rester stable pendant la durée de vie d'une application. Une façon de le faire dans OOP (pseudo-code):

public class Calculator {
    public property VAT { get; private set; }
    public Calculator(int vat) {
        this.VAT = vat;
    }
    public int Calculate(int price) {
        return price * this.VAT;
    }
}

En gros, vous transmettez une valeur de TVA à votre constructeur et votre méthode de calcul peut opérer sur celle-ci via fermeture. Maintenant, au lieu d'utiliser une classe/constructeur, transmettez votre TVA en tant qu'argument dans une fonction. Parce que le seul élément qui vous intéresse est le calcul lui-même, renvoie une nouvelle fonction, qui est la méthode de calcul:

function calculator(vat) {
    return function(item) {
        return item * vat;
    }
}
var calculate = calculator(1.10);
var jsBook = 100; //100$
calculate(jsBook); //110

Dans votre projet, identifiez les valeurs de niveau supérieur qui sont un bon candidat pour déterminer la TVA à calculer. En règle générale, chaque fois que vous passez les mêmes arguments, il existe un moyen de l'améliorer à l'aide de la fermeture. Pas besoin de créer des objets traditionnels.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures

4
roland

Créer un objet

Le moyen le plus simple de créer un objet en JavaScript consiste à utiliser la syntaxe suivante:

var test = {
  a : 5,
  b : 10,
  f : function(c) {
    return this.a + this.b + c;
  }
}

console.log(test);
console.log(test.f(3));

Cela fonctionne très bien pour stocker des données de manière structurée.

Pour des cas d'utilisation plus complexes, cependant, il est souvent préférable de créer des instances de fonctions:

function Test(a, b) {
  this.a = a;
  this.b = b;
  this.f = function(c) {
return this.a + this.b + c;
  };
}

var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));

Cela vous permet de créer plusieurs objets partageant le même "plan", de la même manière que vous utilisez les classes dans, par exemple. Java.

Cela peut encore être fait plus efficacement, cependant, en utilisant un prototype. 

Chaque fois que différentes instances d'une fonction partagent les mêmes méthodes ou propriétés, vous pouvez les déplacer vers le prototype de cet objet. Ainsi, chaque instance d'une fonction a accès à cette méthode ou propriété, mais il n'est pas nécessaire de la dupliquer pour chaque instance.

Dans notre cas, il est logique de déplacer la méthode f vers le prototype:

function Test(a, b) {
  this.a = a;
  this.b = b;
}

Test.prototype.f = function(c) {
  return this.a + this.b + c;
};

var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));

Héritage

Un moyen simple mais efficace de réaliser l'héritage en JavaScript consiste à utiliser les deux lignes suivantes:

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

C'est semblable à faire ceci:

B.prototype = new A();

La principale différence entre les deux réside dans le fait que le constructeur de A n'est pas exécuté lors de l'utilisation de Object.create , qui est plus intuitif et plus similaire à l'héritage basé sur une classe.

Vous pouvez toujours choisir de lancer éventuellement le constructeur de A lors de la création d'une nouvelle instance de B en l'ajoutant au constructeur de B:

function B(arg1, arg2) {
    A(arg1, arg2); // This is optional
}

Si vous souhaitez passer tous les arguments de B à A, vous pouvez également utiliser Function.prototype.apply() :

function B() {
    A.apply(this, arguments); // This is optional
}

Si vous souhaitez mélanger un autre objet dans la chaîne de constructeur de B, vous pouvez combiner Object.create avec Object.assign :

B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype);
B.prototype.constructor = B;

Démo

function A(name) {
  this.name = name;
}

A.prototype = Object.create(Object.prototype);
A.prototype.constructor = A;

function B() {
  A.apply(this, arguments);
  this.street = "Downing Street 10";
}

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

function mixin() {

}

mixin.prototype = Object.create(Object.prototype);
mixin.prototype.constructor = mixin;

mixin.prototype.getProperties = function() {
  return {
    name: this.name,
    address: this.street,
    year: this.year
  };
};

function C() {
  B.apply(this, arguments);
  this.year = "2018"
}

C.prototype = Object.assign(Object.create(B.prototype), mixin.prototype);
C.prototype.constructor = C;

var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());


Remarque

Object.create peut être utilisé en toute sécurité dans tous les navigateurs modernes, y compris IE9 +. Object.assign ne fonctionne dans aucune version de IE ni dans certains navigateurs mobiles. Il est recommandé de polyfillObject.create et/ou Object.assign si vous souhaitez les utiliser et prendre en charge les navigateurs qui ne les implémentent pas.

Vous pouvez trouver un polyfill pour Object.createici et un pour Object.assignici .

3
John Slegers

Je voudrais mentionner que nous pouvons utiliser un titre ou une chaîne pour déclarer un objet.
Il existe différentes manières d’appeler chaque type d’entre elles. Voir ci-dessous:

var test = {

  useTitle : "Here we use 'a Title' to declare an Object",
  'useString': "Here we use 'a String' to declare an Object",
  
  onTitle : function() {
    return this.useTitle;
  },
  
  onString : function(type) {
    return this[type];
  }
  
}

console.log(test.onTitle());
console.log(test.onString('useString'));

0
Chetabahana

Vous pouvez aussi essayer ceci 

    function Person(obj) {
    'use strict';
    if (typeof obj === "undefined") {
        this.name = "Bob";
        this.age = 32;
        this.company = "Facebook";
    } else {
        this.name = obj.name;
        this.age = obj.age;
        this.company = obj.company;
    }

}

Person.prototype.print = function () {
    'use strict';
    console.log("Name: " + this.name + " Age : " + this.age + " Company : " + this.company);
};

var p1 = new Person({name: "Alex", age: 23, company: "Google"});
p1.print();
0
var Klass = function Klass() {
    var thus = this;
    var somePublicVariable = x
      , somePublicVariable2 = x
      ;
    var somePrivateVariable = x
      , somePrivateVariable2 = x
      ;

    var privateMethod = (function p() {...}).bind(this);

    function publicMethod() {...}

    // export precepts
    this.var1 = somePublicVariable;
    this.method = publicMethod;

    return this;
};

Tout d'abord, vous pouvez modifier vos préférences en ajoutant méthodes à l'instance plutôt que l'objet prototype du constructeur. Je déclare presque toujours les méthodes à l'intérieur du constructeur car j'utilise très souvent Constructor Hijacking à des fins liées à l'héritage et aux décorateurs.

Voici comment je décide où les déclarations sont écrites:

  • Ne jamais déclarer une méthode directement sur l'objet de contexte (this)
  • Laisser les déclarations var prévaloir sur les déclarations function
  • Laisser les primitives sur les objets ({} et [])
  • Laisser les déclarations public prévaloir sur les déclarations private
  • Préférez Function.prototype.bind au lieu de thus, self, vm, etc
  • Évitez de déclarer une classe dans une autre classe, sauf si:
    • Il devrait être évident que les deux sont inséparables
    • La classe interne implémente le modèle de commande
    • La classe Inner implémente le motif Singleton
    • La classe Inner implémente le modèle d'état
    • The Inner Class implémente un autre motif de conception qui le garantit
  • Toujours renvoyer this à l'intérieur de le champ d'application lexical de l'espace de fermeture.

Voici pourquoi ces aides:

var Super = function Super() {
    ...
    this.inherited = true;
    ...
};
var Klass = function Klass() {
    ...
    // export precepts
    Super.apply(this);  // extends this with property `inherited`
    ...
};
var Model = function Model(options) {
    var options = options || {};

    this.id = options.id || this.id || -1;
    this.string = options.string || this.string || "";
    // ...

    return this;
};
var model = new Model({...});
var updated = Model.call(model, { string: 'modified' });
(model === updated === true);  // > true
var Singleton = new (function Singleton() {
    var INSTANCE = null;

    return function Klass() {
        ...
        // export precepts
        ...

        if (!INSTANCE) INSTANCE = this;
        return INSTANCE;
    };
})();
var a = new Singleton();
var b = new Singleton();
(a === b === true);  // > true

Comme vous pouvez le constater, je n’ai vraiment pas besoin de thus puisque je préfère Function.prototype.bind (ou .call ou .apply) à thus. Dans notre classe Singleton, nous ne l'appelons même pas thus car INSTANCE véhicule plus d'informations. Pour Model, nous retournons this afin de pouvoir appeler le constructeur à l'aide de .call pour renvoyer l'instance que nous lui avons transmise. De manière redondante, nous l'avons affecté à la variable updated, bien que cela soit utile dans d'autres scénarios.

Parallèlement, je préfère construire des littéraux d'objet en utilisant le mot clé new au lieu de {crochets}:

var klass = new (function Klass(Base) {
    ...
    // export precepts
    Base.apply(this);  //
    this.override = x;
    ...
})(Super);
var klass = Super.apply({
    override: x
});

Comme vous pouvez le constater, ce dernier n'a pas la capacité de remplacer la propriété "override" de sa super-classe.

Si j'ajoute des méthodes à l'objet prototype de la classe, je préfère un littéral d'objet, avec ou sans le mot clé new:

Klass.prototype = new Super();
// OR
Klass.prototype = new (function Base() {
    ...
    // export precepts
    Base.apply(this);
    ...
})(Super);
// OR
Klass.prototype = Super.apply({...});
// OR
Klass.prototype = {
    method: function m() {...}
};
Klass.prototype.method = function m() {...};
0
Cody
var Person = function (lastname, age, job){
this.name = name;
this.age = age;
this.job = job;
this.changeName = function(name){
this.lastname = name;
}
}
var myWorker = new Person('Adeola', 23, 'Web Developer');
myWorker.changeName('Timmy');

console.log("New Worker" + myWorker.lastname);
0
adeola olanrewaju

En plus de la réponse acceptée de 2009. Si vous pouvez cibler les navigateurs modernes, vous pouvez utiliser Object.defineProperty

La méthode Object.defineProperty () définit une nouvelle propriété directement sur un objet, ou modifie une propriété existante sur un objet, et retourne L'object. Source: Mozilla

var Foo = (function () {
    function Foo() {
        this._bar = false;
    }
    Object.defineProperty(Foo.prototype, "bar", {
        get: function () {
            return this._bar;
        },
        set: function (theBar) {
            this._bar = theBar;
        },
        enumerable: true,
        configurable: true
    });
    Foo.prototype.toTest = function () {
        alert("my value is " + this.bar);
    };
    return Foo;
}());

// test instance
var test = new Foo();
test.bar = true;
test.toTest();

Pour voir une liste de compatibilité de bureau et mobile, voir Liste de compatibilité de navigateur de Mozilla . Oui, IE9 + le supporte aussi bien que Safari mobile. 

0
Alex Nolasco