web-dev-qa-db-fra.com

Constructeurs dans des objets JavaScript

Les classes/objets JavaScript peuvent-ils avoir des constructeurs? Comment sont-ils créés?

399
Click Upvote

Utilisation de prototypes:

function Box(color) // Constructor
{
    this.color = color;
}

Box.prototype.getColor = function()
{
    return this.color;
};

Masquer la "couleur" (ressemble quelque peu à une variable membre privée):

function Box(col)
{
   var color = col;

   this.getColor = function()
   {
       return color;
   };
}

Usage:

var blueBox = new Box("blue");
alert(blueBox.getColor()); // will alert blue

var greenBox = new Box("green");
alert(greenBox.getColor()); // will alert green
406
Nick

Voici un modèle que j'utilise parfois pour un comportement similaire à la POO en JavaScript. Comme vous pouvez le constater, vous pouvez simuler des membres privés (statiques et par exemple) à l'aide de fermetures. Ce que new MyClass() retournera est un objet avec uniquement les propriétés affectées à l'objet this et dans l'objet prototype de la "classe".

var MyClass = (function () {
    // private static
    var nextId = 1;

    // constructor
    var cls = function () {
        // private
        var id = nextId++;
        var name = 'Unknown';

        // public (this instance only)
        this.get_id = function () { return id; };

        this.get_name = function () { return name; };
        this.set_name = function (value) {
            if (typeof value != 'string')
                throw 'Name must be a string';
            if (value.length < 2 || value.length > 20)
                throw 'Name must be 2-20 characters long.';
            name = value;
        };
    };

    // public static
    cls.get_nextId = function () {
        return nextId;
    };

    // public (shared across instances)
    cls.prototype = {
        announce: function () {
            alert('Hi there! My id is ' + this.get_id() + ' and my name is "' + this.get_name() + '"!\r\n' +
                  'The next fellow\'s id will be ' + MyClass.get_nextId() + '!');
        }
    };

    return cls;
})();

On m'a posé des questions sur l'héritage en utilisant ce modèle, alors voici:

// It's a good idea to have a utility class to wire up inheritance.
function inherit(cls, superCls) {
    // We use an intermediary empty constructor to create an
    // inheritance chain, because using the super class' constructor
    // might have side effects.
    var construct = function () {};
    construct.prototype = superCls.prototype;
    cls.prototype = new construct;
    cls.prototype.constructor = cls;
    cls.super = superCls;
}

var MyChildClass = (function () {
    // constructor
    var cls = function (surName) {
        // Call super constructor on this instance (any arguments
        // to the constructor would go after "this" in call(…)).
        this.constructor.super.call(this);

        // Shadowing instance properties is a little bit less
        // intuitive, but can be done:
        var getName = this.get_name;

        // public (this instance only)
        this.get_name = function () {
            return getName.call(this) + ' ' + surName;
        };
    };
    inherit(cls, MyClass); // <-- important!

    return cls;
})();

Et un exemple pour tout utiliser:

var bob = new MyClass();
bob.set_name('Bob');
bob.announce(); // id is 1, name shows as "Bob"

var john = new MyChildClass('Doe');
john.set_name('John');
john.announce(); // id is 2, name shows as "John Doe"

alert(john instanceof MyClass); // true

Comme vous pouvez le constater, les classes interagissent correctement les unes avec les autres (elles partagent l'id statique de MyClass, la méthode announce utilise la méthode get_name correcte, etc.).

Une chose à noter est la nécessité d'observer les propriétés de l'instance. Vous pouvez réellement faire en sorte que la fonction inherit parcoure toutes les propriétés d'instance (à l'aide de hasOwnProperty) qui sont des fonctions et ajoute automatiquement une propriété super_<method name>. Cela vous permettrait d'appeler this.super_get_name() au lieu de la stocker dans une valeur temporaire et de l'appeler lié à l'aide de call.

Pour les méthodes sur le prototype, vous n'avez pas besoin de vous soucier de ce qui précède. Si vous souhaitez accéder aux méthodes de prototype de la classe supérieure, vous pouvez simplement appeler this.constructor.super.prototype.methodName. Si vous voulez le rendre moins bavard, vous pouvez bien sûr ajouter des propriétés pratiques. :)

248
Blixt

Il me semble que la plupart d’entre vous donnez des exemples de getters et de setters qui ne sont pas des constructeurs, c’est-à-dire http://en.wikipedia.org/wiki/Constructor_ (object-oriented_programming) .

lunched-dan était plus proche mais l'exemple ne fonctionnait pas dans jsFiddle.

Cet exemple crée une fonction constructeur privée qui ne s'exécute que lors de la création de l'objet.

var color = 'black';

function Box()
{
   // private property
   var color = '';

   // private constructor 
   var __construct = function() {
       alert("Object Created.");
       color = 'green';
   }()

   // getter
   this.getColor = function() {
       return color;
   }

   // setter
   this.setColor = function(data) {
       color = data;
   }

}

var b = new Box();

alert(b.getColor()); // should be green

b.setColor('orange');

alert(b.getColor()); // should be orange

alert(color); // should be black

Si vous souhaitez affecter des propriétés publiques, le constructeur peut être défini comme suit:

var color = 'black';

function Box()
{
   // public property
   this.color = '';

   // private constructor 
   var __construct = function(that) {
       alert("Object Created.");
       that.color = 'green';
   }(this)

   // getter
   this.getColor = function() {
       return this.color;
   }

   // setter
   this.setColor = function(color) {
       this.color = color;
   }

}

var b = new Box();

alert(b.getColor()); // should be green

b.setColor('orange'); 

alert(b.getColor()); // should be orange

alert(color); // should be black
166
Jon

Alors, quel est le point de la propriété "constructeur"? Vous ne savez pas où cela pourrait être utile, des idées?

Le but de la propriété constructeur est de fournir un moyen de prétendre que JavaScript a des classes. Une des choses que vous ne pouvez pas faire est de changer le constructeur d'un objet après sa création. C'est compliqué.

J'ai écrit un article assez complet à ce sujet il y a quelques années: http://joost.zeekat.nl/constructors-considered-mildly-confusing.html

23
Joost Diepenmaat

Exemple ici: http://jsfiddle.net/FZ5nC/

Essayez ce modèle:

<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Name = Name||{};
Name.Space = Name.Space||{};

//============================================================
// Constructor - MUST BE AT TOP OF FILE
//------------------------------------------------------------
Name.Space.ClassName = function Name_Space_ClassName(){}

//============================================================
// Member Functions & Variables
//------------------------------------------------------------
Name.Space.ClassName.prototype = {
  v1: null
 ,v2: null
 ,f1: function Name_Space_ClassName_f1(){}
}

//============================================================
// Static Variables
//------------------------------------------------------------
Name.Space.ClassName.staticVar = 0;

//============================================================
// Static Functions
//------------------------------------------------------------
Name.Space.ClassName.staticFunc = function Name_Space_ClassName_staticFunc(){
}
</script>

Vous devez ajuster votre espace de noms si vous définissez une classe statique:

<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Shape = Shape||{};
Shape.Rectangle = Shape.Rectangle||{};
// In previous example, Rectangle was defined in the constructor.
</script>

Exemple de classe:

<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Shape = Shape||{};

//============================================================
// Constructor - MUST BE AT TOP OF FILE
//------------------------------------------------------------
Shape.Rectangle = function Shape_Rectangle(width, height, color){
    this.Width = width;
    this.Height = height;
    this.Color = color;
}

//============================================================
// Member Functions & Variables
//------------------------------------------------------------
Shape.Rectangle.prototype = {
  Width: null
 ,Height: null
 ,Color: null
 ,Draw: function Shape_Rectangle_Draw(canvasId, x, y){
    var canvas = document.getElementById(canvasId);
    var context = canvas.getContext("2d");
    context.fillStyle = this.Color;
    context.fillRect(x, y, this.Width, this.Height);
 }
}

//============================================================
// Static Variables
//------------------------------------------------------------
Shape.Rectangle.Sides = 4;

//============================================================
// Static Functions
//------------------------------------------------------------
Shape.Rectangle.CreateSmallBlue = function Shape_Rectangle_CreateSmallBlue(){
    return new Shape.Rectangle(5,8,'#0000ff');
}
Shape.Rectangle.CreateBigRed = function Shape_Rectangle_CreateBigRed(){
    return new Shape.Rectangle(50,25,'#ff0000');
}
</script>

Exemple d'instanciation:

<canvas id="painting" width="500" height="500"></canvas>
<script>
alert("A rectangle has "+Shape.Rectangle.Sides+" sides.");

var r1 = new Shape.Rectangle(16, 12, "#aa22cc");
r1.Draw("painting",0, 20);

var r2 = Shape.Rectangle.CreateSmallBlue();
r2.Draw("painting", 0, 0);

Shape.Rectangle.CreateBigRed().Draw("painting", 10, 0);
</script>

Les fonctions de notification sont définies comme suit: A.B = fonction A_B (). Cela facilite le débogage de votre script. Ouvrez le panneau Inspecter un élément de Chrome, exécutez ce script et développez la trace de débogage:

<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Fail = Fail||{};

//============================================================
// Static Functions
//------------------------------------------------------------
Fail.Test = function Fail_Test(){
    A.Func.That.Does.Not.Exist();
}

Fail.Test();
</script>
16
bitlather

C'est un constructeur:

function MyClass() {}

Quand tu fais

var myObj = new MyClass();

MyClass est exécuté et un nouvel objet est renvoyé pour cette classe.

10
Satish N Ramteare

Oui, vous pouvez définir un constructeur dans une déclaration de classe comme ceci:

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}
8
Bruno

J'ai trouvé ce tutoriel très utile. Cette approche est utilisée par la plupart des plug-ins jQuery.

http://www.htmlgoodies.com/html5/tutorials/create-an-object-oriented-javascript-class-constructor.html#fbid=OVYAQL_TDpK

var Class = function(methods) {   
    var klass = function() {    
        this.initialize.apply(this, arguments);          
    };  

    for (var property in methods) { 
       klass.prototype[property] = methods[property];
    }

    if (!klass.prototype.initialize) klass.prototype.initialize = function(){};      

    return klass;    
};

Maintenant ,

var Person = Class({ 
    initialize: function(name, age) {
        this.name = name;
        this.age  = age;
    },
    toString: function() {
        return "My name is "+this.name+" and I am "+this.age+" years old.";
    }
}); 

var alice = new Person('Alice', 26);
alert(alice.name); //displays "Alice"
alert(alice.age); //displays "26"
alert(alice.toString()); //displays "My name is Alice and I am 26 years old" in most browsers.
//IE 8 and below display the Object's toString() instead! "[Object object]"
8
anasanjaria

Ce modèle m'a bien servi. Avec ce modèle, vous créez des classes dans des fichiers séparés, vous les chargez dans votre application globale "au besoin".

// Namespace
// (Creating new if not instantiated yet, otherwise, use existing and just add to it)
var myApp = myApp || {};

// "Package" 
// Similar to how you would establish a package in other languages
(function() {

// "Class"
var MyClass = function(params) {
    this.initialize(params);
}

    // "Private Static" vars 
    //    - Only accessible to functions in this class.
    //    - Doesn't get wiped out when we create a new instance.
    var countInstances = 0;
    var allInstances = [];

    // "Private Static" functions 
    //    - Same as above, but it's a function accessible 
    //      only to other functions in this class.
    function doSomething(){
    }

    // "Public Static" vars
    //    - Everyone has access.
    //    - Doesn't get wiped out when we create a new instance.
    MyClass.counter = 0;

    // "Public Static" functions
    //    - Same as above, but anyone can call this "static method".
    //    - Kinda like a singleton class situation.
    MyClass.foobar = function(){
    }

    // Public properties and methods are built into the "prototype"
    //    - This is how each instance can become unique unto itself.
    //    - Establishing "p" as "local" (Static Private) variable 
    //      simply so we don't have to keep typing "MyClass.prototype" 
    //      for each property and function.
var p = MyClass.prototype;

    // "Public" vars
    p.id = null;
    p.firstname = null;
    p.lastname = null;

    // "Private" vars
    //    - Only used by "this" instance.
    //    - There isn't "true" privacy for each 
    //      instance so we have to fake it. 
    //    - By tradition, we indicate "privacy"  
    //      by prefixing it with an underscore. 
    //    - So technically, anyone can access, but we simply 
    //      don't tell anyone about it (e.g. in your API)
    //      so no one knows about it :)
    p._foo = null;

    p.initialize = function(params){
        this.id = MyClass.counter++;
        this.firstname = params.firstname;
        this.lastname = params.lastname;
        MyClass.counter++;
        countInstances++;
        allInstances.Push(this);
    }

    p.doAlert = function(theMessage){
        alert(this.firstname + " " + this.lastname + " said: " + theMessage + ". My id:" + this.id + ".  Total People:" + countInstances + ". First Person:" + allInstances[0].firstname + " " + allInstances[0].lastname);
    }


// Assign class to app
myApp.MyClass = MyClass;

// Close the "Package"
}());

// Usage example:
var bob = new myApp.MyClass({   firstname   :   "bob",
                                lastname    :   "er"
                            });

bob.doAlert("hello there");
8
bob

Je suppose que je posterai ce que je fais avec la fermeture javascript car personne n’utilise encore la fermeture.

var user = function(id) {
  // private properties & methods goes here.
  var someValue;
  function doSomething(data) {
    someValue = data;
  };

  // constructor goes here.
  if (!id) return null;

  // public properties & methods goes here.
  return {
    id: id,
    method: function(params) {
      doSomething(params);
    }
  };
};

Les commentaires et suggestions à cette solution sont les bienvenus. :)

6
Hendra Uzia

Peut-être que c'est un peu simplifié, mais voici ce que je propose en 2017:

class obj {
  constructor(in_shape, in_color){
    this.shape = in_shape;
    this.color = in_color;
  }

  getInfo(){
    return this.shape + ' and ' + this.color;
  }
  setShape(in_shape){
    this.shape = in_shape;
  }
  setColor(in_color){
    this.color = in_color;
  }
}

En utilisant la classe ci-dessus, j'ai les éléments suivants:

var newobj = new obj('square', 'blue');

//Here, we expect to see 'square and blue'
console.log(newobj.getInfo()); 

newobj.setColor('white');
newobj.setShape('sphere');

//Since we've set new color and shape, we expect the following: 'sphere and white'
console.log(newobj.getInfo());

Comme vous pouvez le constater, le constructeur prend deux paramètres et définit les propriétés de l'objet. Nous modifions également la couleur et la forme de l'objet à l'aide des fonctions setter et nous prouvons que son changement a été conservé lors de l'appel de getInfo() après ces modifications.

Un peu tard, mais j'espère que cela aide. J'ai testé cela avec un mocha unité-test, et ça fonctionne bien.

4
Chim Chimz

En utilisant l'exemple de Nick ci-dessus, vous pouvez créer un constructeur pour les paramètres sans en utilisant une instruction return comme dernière instruction de votre définition d'objet. Renvoyez votre fonction constructeur comme ci-dessous et il exécutera le code dans __construct chaque fois que vous créez l'objet:

function Box()
{
   var __construct = function() {
       alert("Object Created.");
       this.color = 'green';
   }

  this.color = '';

   this.getColor = function() {
       return this.color;
   }

   __construct();
}

var b = new Box();
4
Dan Power

Ils le font si vous utilisez TypeScript - Open Source de Microsoft :-)

class BankAccount {
 balance: number;
 constructor(initially: number) {
 this.balance = initially;
 }
 deposit(credit: number) {
 this.balance += credit;
 return this.balance;
 }
}

TypeScript vous permet de "simuler" les constructions OO compilées en constructions javascript. Si vous démarrez un projet de grande envergure, vous gagnerez peut-être beaucoup de temps et la version 1.0 vient d'être franchie.

http://www.typescriptlang.org/Content/TypeScript%20Language%20Specification.pdf

Le code ci-dessus est "compilé" pour:

var BankAccount = (function () {
    function BankAccount(initially) {
        this.balance = initially;
    }
    BankAccount.prototype.deposit = function (credit) {
        this.balance += credit;
        return this.balance;
    };
    return BankAccount;
})();
3
Simon_Weaver

En JavaScript, le type d'appel définit le comportement de la fonction:

  • Invocation directe func()
  • Invocation de méthode sur un objet obj.func()
  • Constructeur invocation new func()
  • Appel indirect func.call() ou func.apply()

La fonction est appelée en tant que constructeur lors d'un appel à l'aide de l'opérateur new:

function Cat(name) {
   this.name = name;
}
Cat.prototype.getName = function() {
   return this.name;
}

var myCat = new Cat('Sweet'); // Cat function invoked as a constructor

Toute instance ou objet prototype en JavaScript a une propriété constructor, qui fait référence à la fonction constructeur.

Cat.prototype.constructor === Cat // => true
myCat.constructor         === Cat // => true

Vérifiez cet article à propos de la propriété du constructeur.

1
Dmitri Pavlutin

juste pour offrir un peu de variété. ds.oop est un bon moyen de déclarer des classes avec des constructeurs en javascript. Il prend en charge tous les types d'héritage possibles (y compris 1 type que même c # ne prend pas en charge), ainsi que les interfaces Nice.

var Color = ds.make.class({
    type: 'Color',
    constructor: function (r,g,b) { 
        this.r = r;                     /* now r,g, and b are available to   */
        this.g = g;                     /* other methods in the Color class  */
        this.b = b;                     
    }
});
var red = new Color(255,0,0);   // using the new keyword to instantiate the class
0
dss

http://www.jsoops.net/ est assez bon pour oop dans Js. Si vous fournissez une variable et une fonction privées, protégées, publiques, ainsi qu'une fonctionnalité d'héritage. Exemple de code:

var ClassA = JsOops(function (pri, pro, pub)
{// pri = private, pro = protected, pub = public

    pri.className = "I am A ";

    this.init = function (var1)// constructor
    {
        pri.className += var1;
    }

    pub.getData = function ()
    {
        return "ClassA(Top=" + pro.getClassName() + ", This=" + pri.getClassName()
        + ", ID=" + pro.getClassId() + ")";
    }

    pri.getClassName = function () { return pri.className; }
    pro.getClassName = function () { return pri.className; }
    pro.getClassId = function () { return 1; }
});

var newA = new ClassA("Class");

//***Access public function
console.log(typeof (newA.getData));
// function
console.log(newA.getData());
// ClassA(Top=I am A Class, This=I am A Class, ID=1)

//***You can not access constructor, private and protected function
console.log(typeof (newA.init));            // undefined
console.log(typeof (newA.className));       // undefined
console.log(typeof (newA.pro));             // undefined
console.log(typeof (newA.getClassName));    // undefined
0
user1624059

Ici, nous devons noter un point dans le script Java, il s’agit d’un langage sans classe, mais nous pouvons le réaliser en utilisant des fonctions dans le script Java. La manière la plus courante d’atteindre cet objectif consiste à créer une fonction dans le script Java et à utiliser newmot-clé pour créer un objet et utiliser thismot-clé pour définir la propriété et les méthodes. Voici l'exemple.

// Function constructor

   var calculator=function(num1 ,num2){
   this.name="This is function constructor";
   this.mulFunc=function(){
      return num1*num2
   };

};

var objCal=new calculator(10,10);// This is a constructor in Java script
alert(objCal.mulFunc());// method call
alert(objCal.name);// property call

//Constructors With Prototypes

var calculator=function(){
   this.name="Constructors With Prototypes";
};

calculator.prototype.mulFunc=function(num1 ,num2){
 return num1*num2;
};
var objCal=new calculator();// This is a constructor in Java script
alert(objCal.mulFunc(10,10));// method call
alert(objCal.name); // property call
0
Sheo Dayal Singh

En utilisant le modèle génial de Blixt vu ci-dessus, j'ai découvert qu'il ne fonctionnait pas bien avec l'héritage multiniveau (MyGrandChildClass, extension de MyChildClass, extension de MyClass). Il effectue un cycle d'appel continu du constructeur du premier parent. Voici donc une solution de contournement simple: si vous avez besoin d'un héritage multiniveau, au lieu d'utiliser this.constructor.super.call(this, surName);, utilisez chainSuper(this).call(this, surName); avec la fonction de chaîne définie comme suit:

function chainSuper(cls) {
  if (cls.__depth == undefined) cls.__depth = 1; else cls.__depth++;
  var depth = cls.__depth;
  var sup = cls.constructor.super;
  while (depth > 1) {
    if (sup.super != undefined) sup = sup.super;
    depth--;
  }
  return sup;
}
0
jan