web-dev-qa-db-fra.com

Quelle est la méthode recommandée pour créer des objets dans NodeJS?

Je construis un composite pour le module request, mais je ne suis pas sûr de la meilleure pratique concernant la création d'objets dans JS for Node.

Option 1:

function RequestComposite(request) {
  return {
    get: function (url) { return request.get(url); }
  }
}
var comp = RequestComposite(request);
  • Note: Je sais que je devrais appeler un CB de manière asynchrone, mais pour la facilité de l'explication, je le retourne ...

Option 2:

function RequestComposite(request) {
  this.request = request;
}

RequestComposite.prototype.get = function (url) { return this.request.get(url); };
var comp = new RequestComposite(request);

Option 3:

var RequestComposite = {
  init: function (request) { this.request = request; },
  get: function (url) { return request.get(url); }
}
var comp = Object.create(RequestComposite).init(request);

J'essayais de me débrouiller, mais je devenais encore plus confus quant à l'utilisation des objets ...

La réponse serait-elle différente si je veux utiliser des objets pour les navigateurs?

Merci.

29
tounano

Le moyen le plus efficace est le suivant:

  • Définissez uniquement les propriétés dans le constructeur.

  • Définissez les méthodes dans la propriété .prototype du constructeur. Pourquoi? car cela empêche de réécrire chaque méthode chaque fois que vous créez un objet. De cette façon, vous recyclez le même prototype pour chaque objet que vous créez. Efficace en mémoire et dans le temps.

  • N'utilisez pas de fermetures pour des propriétés privées. Pourquoi ?: Il est lent et vous empêche d'utiliser cet objet dans une chaîne d'héritage (les vars pseudo-privés n'appartiennent pas à l'objet, ils sont simplement accessibles). Utilisez à la place un trait de soulignement pour indiquer qu'il s'agit d'une propriété privée à laquelle vous ne devriez pas accéder de l'extérieur.

  • Utilisez new au lieu de Object.create. C'est plus rapide et à la fin, Object.create utilise new sous le capot.

En d'autres termes, quelque chose comme ceci:

var Person = function (name) {
    this._name = name;
};

Person.prototype.sayHello = function () {
    alert('My name is: ' + this._name);
};

var john = new Person('John');
john.sayHello();

MODIFIER

Quelques informations supplémentaires:

  • Object.create vs new. Benchmark ici . Bien que la question concerne node.js, je pense que le même comportement est à prévoir. (toute correction est la bienvenue)

  • Fermetures pour émuler des propriétés privées: Vous pouvez lire à propos de dans cette question. . Le point où les propriétés privées/de fermeture n'appartiennent pas à l'objet est un fait de programmation: elles sont accessibles par les méthodes d'objet mais n'appartiennent pas à l'objet. Lorsque vous utilisez l'héritage, c'est un gros désastre. De plus, seules les méthodes déclarées dans le constructeur ont accès à la fermeture. Les méthodes définies dans le prototype ne le sont pas.

  • Définir des méthodes dans le constructeur ou la propriété prototype: lisez cette question , et jetez un coup d'œil à ce point de repère

EDIT 15/04/2016

Les points que j’ai soulevés ici il ya trois ans sont toujours justes du point de vue de la performance, mais mon opinion sur la "méthode recommandée" a légèrement changé entre-temps. Les fonctions d'usine sont en général une bonne option, ce qui constituerait la première approche du PO. Juste un exemple:

function Person(name) {
    return {
        sayHello: function () { alert('My name is: ' + name); }
    };
}

et puis juste faire:

var p = Person('John');

Dans ce cas, vous négociez souplesse (pas de couplage new, facilité de composition avec d'autres "mix-ins") et de simplicité (pas de désordre this, instanciation d'objet facile) contre de la vitesse et de la mémoire. En général, ils sont parfaitement valables. Si vous rencontrez des problèmes de performances, et qu’ils sont dus à cette manière de créer des objets, utilisez une autre méthode. L'approche Object.create est également bonne, tombant en quelque sorte au milieu de new et des fonctions d'usine (remarque: la nouvelle syntaxe class est un sucre syntaxique pour new + prototype)

Pour résumer: je recommande de commencer par le moyen le plus simple et le plus simple de créer des objets (fonctions d'usine), puis de recourir à d'autres méthodes lorsque vous rencontrez des problèmes de performances (ce qui dans la plupart des cas n'est jamais).

45
bgusach

Il existe de nombreuses façons de créer "Class" et "Object" dans JS. Je préfère cette façon:

var MyObject =
        function(args) {
            // Private
            var help = "/php/index.php?method=getHelp";
            var schedule = "/php/index.php?method=getSchedules";
            var ajax = function(url, callback, type) {
                //....
            }

            // Public
            this.property = 0;
            this.getInfo = function() {
                // ... 
            }

            // Constructor
            function(data) {
               this.property = data;
           }(args);
        };

var o = new MyObject();
7
ovnia

Remarque: Si vous êtes plus familier avec la syntaxe OOP, vous pouvez également utiliser class , qui n'est qu'un sucre syntaxique par rapport à la méthode existante basée sur un prototype.

Performance Comparaison entre 4 façons de créer un objet - avec constructeur (Chrome 61 - https://jsperf.com/create-object-ways )

Option A: Utilisation de return (La plus rapide 3x)

Option B: Utilisation de {key:value} (1.5x)

Option C: Utilisation de prototype (1x) <- Base

Option D: Utilisation de class (1.02x)

Option A semble fonctionner mieux. Notez que l’augmentation des performances tient en partie au fait qu’elle évite l’utilisation de new ou object.create. Donc, juste pour avoir un procès équitable, voici un autre test entre les objets de méthode uniquement, sans constructeur ni propriétés.


Performance Comparaison entre 4 façons de créer un objet contenant uniquement des méthodes (Chrome 61 - https://jsperf.com/create-static-object-ways )

Option A: Utiliser return (3.2x)

Option B: Utiliser {key:value} (la plus rapide des 3.3x)

Option C: Utilisation de prototype (1.8x)

Option D: Utilisation de class (1.9x)

Option B surperforme un peu Option A. De plus, le goulot d'étranglement causé par object.create était supérieur à new.


Meilleur entrainement

Option A (en utilisant return) est la plus performante dans les deux scénarios. De cette façon, vous pouvez avoir un peu de désordre si vous avez plusieurs méthodes et propriétés.

Je préfère diviser le constructeur et les propriétés en un objet séparé à l'aide de Option A et des méthodes dans un autre objet à l'aide de Option B. Cette approche nécessite l'envoi d'une référence instance supplémentaire dans les paramètres, mais elle peut être utile si vous avez plusieurs objets utilisant les mêmes propriétés et constructeurs (il est également possible d'obtenir un héritage OOP).

Exemple:

// Constructor & Properties Object (Using option A)
var UserData = function(request){

  // Constructor
  if ( request.name )
    var name = request.name;
  else
    var name = 'Not Available';

  if ( request.age )
    var age = request.age;
  else
    var age = null;


  // Return properties
  return {
    userName: name,
    userAge: age
  };

};


// Object methods (Using Option B)
var Adults = {

  printName: function(instance){ // Read propery example
    console.log( 'Mr. ' + instance.userName );
  },

  changeName: function(instance, newName){ // Write property example
    instance.userName = newName;
  },

  foo: function(){
    console.log( 'foo' );
  }

};


// Object methods (Using Option B)
var Children = {

  printName: function(instance){
    console.log( 'Master ' + instance.userName );
  },

  bar: function(){
    console.log( 'bar' );
  }

}


// Initialize
var userData = UserData ( {name: 'Doe', age: 40} );

// Call methods
Adults.printName(userData); // Output 'Mr. Doe'
Children.printName(userData); // Output 'Master Doe'

Adults.foo(); // Output 'foo'
Children.bar(); // Output 'bar'

Adults.changeName(userData, 'John');
Adults.printName(userData); // Output 'Mr. John'
1
SJ00