428 votes

Comprendre la différence entre Object.create() et new SomeFunction()

Je suis récemment tombé sur le Object.create() en JavaScript, et j'essaie de déduire en quoi elle est différente de la création d'une nouvelle instance d'un objet avec la méthode new SomeFunction() et quand vous voudrez utiliser l'un plutôt que l'autre.

Prenons l'exemple suivant :

var test = {
  val: 1,
  func: function() {
    return this.val;
  }
};
var testA = Object.create(test);

testA.val = 2;
console.log(test.func()); // 1
console.log(testA.func()); // 2

console.log('other test');
var otherTest = function() {
  this.val = 1;
  this.func = function() {
    return this.val;
  };
};

var otherTestA = new otherTest();
var otherTestB = new otherTest();
otherTestB.val = 2;
console.log(otherTestA.val); // 1 
console.log(otherTestB.val); // 2

console.log(otherTestA.func()); // 1
console.log(otherTestB.func()); // 2

Remarquez que le même comportement est observé dans les deux cas. Il me semble que les principales différences entre ces deux scénarios sont :

  • L'objet utilisé dans Object.create() forme en fait le prototype du nouvel objet, alors que dans la version new Function() des propriétés/fonctions déclarées ne forment pas le prototype.
  • Vous ne pouvez pas créer de fermetures avec l'option Object.create() comme vous le feriez avec la syntaxe fonctionnelle. Ceci est logique étant donné l'étendue du type lexical (vs bloc) de JavaScript.

Les déclarations ci-dessus sont-elles correctes ? Et est-ce que quelque chose m'échappe ? Quand utiliseriez-vous l'un plutôt que l'autre ?

EDIT : lien vers la version jsfiddle de l'échantillon de code ci-dessus : http://jsfiddle.net/rZfYL/

3 votes

1 votes

Quelle est la différence entre new Object() et la notation littérale des objets ? est également liée, c'est-à-dire qu'il s'agit de comparer ce qui est nouveau, ce qui est créé et ce qui est juste. {}

489voto

Evi1M4chine Points 509

Très simplement dit, new X es Object.create(X.prototype) avec en plus l'exécution du constructor fonction. (Et en donnant à la constructor l'occasion de return l'objet réel qui devrait être le résultat de l'expression au lieu de this .)

C'est tout. :)

Le reste des réponses sont juste confuses, parce qu'apparemment personne d'autre ne lit la définition de nouveau soit. ;)

27 votes

+1 Simplicité et clarté ! (Bien que Object.create(null) semble être une option intéressante - il faudrait peut-être le mentionner).

1 votes

Il faut rester simple, c'est ce qu'il faut faire.

0 votes

Cela laisse juste la question de "attendez, donc fonctions avoir des prototypes trop ? Quelle est la relation entre ceux-ci et objet des prototypes ?"

256voto

CMS Points 315406

L'objet utilisé dans Object.create forme en fait le prototype du nouvel objet, alors que dans la forme new Function(), les propriétés/fonctions déclarées ne forment pas le prototype.

Oui, Object.create construit un objet qui hérite directement de celui passé en premier argument.

Avec les fonctions de constructeur, l'objet nouvellement créé hérite du prototype du constructeur, par ex :

var o = new SomeConstructor();

Dans l'exemple ci-dessus, o hérite directement de SomeConstructor.prototype .

Il y a une différence ici, avec Object.create vous pouvez créer un objet qui n'hérite de rien, Object.create(null); D'autre part, si vous définissez SomeConstructor.prototype = null; l'objet nouvellement créé héritera de Object.prototype .

Vous ne pouvez pas créer de fermetures avec la syntaxe Object.create comme vous le feriez avec la syntaxe fonctionnelle. Ceci est logique étant donné la portée lexicale (par opposition au bloc) du type JavaScript.

Eh bien, vous pouvez créer des fermetures, par exemple en utilisant l'argument des descripteurs de propriété :

var o = Object.create({inherited: 1}, {
  foo: {
    get: (function () { // a closure
      var closured = 'foo';
      return function () {
        return closured+'bar';
      };
    })()
  }
});

o.foo; // "foobar"

Notez que je parle de l'ECMAScript 5ème édition. Object.create et non la cale de Crockford.

La méthode commence à être implémentée de manière native sur les navigateurs les plus récents, vérifiez ceci tableau de compatibilité .

2 votes

@CMS 2 questions. 1) La chaîne de portée sur Object.create(null) se termine-t-elle toujours à la portée globale (telle que 'window' dans un navigateur), ou se termine-t-elle sur elle-même ? 2) La raison pour laquelle Object.create a été introduit n'est toujours pas claire pour moi (par exemple, quelle fonctionnalité manquait que cela a résolu ?) et pourquoi on l'utiliserait à la place de new Function() ;

10 votes

@Matt, 1) la chaîne de portée n'est pas vraiment un concept lié ici, la chaîne de portée est liée à résolution de l'identifiant par exemple : comment foo; est résolu dans l'actuel environnement lexical . 2) Fournir un moyen facile de mettre en œuvre l'héritage, c'est une construction vraiment puissante. IMO je l'utiliserais parce que c'est vraiment simple et léger, mais pour le code de production, nous devons encore attendre un certain temps jusqu'à ce que ES5 soit largement supporté. A propos des fonctionnalités manquantes, le fait de créer un objet "immaculé", Object.create(null); manquait, il est vraiment utile d'implémenter des objets fiables de type table de hachage...

0 votes

@CMS Merci. Ainsi, lorsque vous créez un objet en utilisant 'Object.create', vous avez la possibilité de sélectionner l'objet qui doit être son prototype.

221voto

Ray Hulha Points 1072

Voici les étapes qui se déroulent en interne pour les deux appels :
(Indice : la seule différence se situe à l'étape 3).


new Test() :

  1. créer new Object() obj.
  2. set obj.__proto__ a Test.prototype
  3. return Test.call(obj) || obj; // normally obj is returned but constructors in JS can return a value

Object.create( Test.prototype )

  1. créer new Object() obj.
  2. set obj.__proto__ a Test.prototype
  3. return obj;

Donc, en gros Object.create n'exécute pas le constructeur.

0 votes

@Ray donc en utilisant object.create nous avons les propriétés de la fonction mentionnée dans la fonction constructeur ?

0 votes

@sortednoun tant que les propriétés sont privées et non spécifiées sur le prototype, Oui, ils ne seront pas hérités et vous ne les aurez pas dans le nouvel objet. (et, j'ajouterais, vous pouvez vous attendre à obtenir d'éventuelles propriétés prototypées du parent, juste lorsque le constructeur du parent a été exécuté au moins une fois).

0 votes

Comme avec la plupart des fonctions de constructeur, les méthodes sont définies dans l'objet retourné, new a fondamentalement toutes les fonctions dupliquées, alors que Object.create ne le fait pas.

63voto

pandit Points 1454

Je vais essayer d'expliquer (plus sur Blog ) :

  1. Lorsque vous écrivez Car Constructeur var Car = function(){} c'est ainsi que les choses se passent en interne : A diagram of prototypal chains when creating javascript objects Nous avons un {prototype} lien caché vers Function.prototype qui n'est pas accessible et un prototype lien vers Car.prototype qui est accessible et possède une constructor de Car . Function.prototype et Car.prototype ont tous deux des liens cachés vers Object.prototype .
  2. Lorsque l'on veut créer deux objets équivalents en utilisant la fonction new et create alors nous devons le faire comme ceci : Honda = new Car(); y Maruti = Object.create(Car.prototype) . A diagram of prototypal chains for differing object creation methods Que se passe-t-il ?

    Honda = new Car(); - Quand vous créez un objet comme celui-ci, alors caché {prototype} est pointé vers la propriété Car.prototype . Donc ici, le {prototype} de l'objet Honda sera toujours Car.prototype - nous n'avons pas la possibilité de changer le {prototype} de l'objet. Que faire si je veux modifier le prototype de notre objet nouvellement créé ?
    Maruti = Object.create(Car.prototype) - Lorsque vous créez un objet de ce type, vous avez une option supplémentaire pour choisir le nom de votre objet. {prototype} propriété. Si vous voulez que Car.prototype soit la propriété {prototype} puis le passer comme paramètre dans la fonction. Si vous ne voulez pas de {prototype} pour votre objet alors vous pouvez passer null comme ça : Maruti = Object.create(null) .

Conclusion - En utilisant la méthode Object.create vous avez la liberté de choisir votre objet {prototype} propriété. Sur new Car(); vous n'avez pas cette liberté.

Méthode préférée en OO JavaScript :

Supposons que nous ayons deux objets a y b .

var a = new Object();
var b = new Object();

Maintenant, supposons a a des méthodes qui b veut également y accéder. Pour cela, nous avons besoin de l'héritage des objets ( a devrait être le prototype de b seulement si nous voulons avoir accès à ces méthodes). Si nous vérifions les prototypes de a y b alors nous découvrirons qu'ils partagent le prototype Object.prototype .

Object.prototype.isPrototypeOf(b); //true
a.isPrototypeOf(b); //false (the problem comes into the picture here).

Problème - nous voulons l'objet a comme le prototype de b mais ici nous avons créé un objet b avec le prototype Object.prototype . Solution - Introduction d'ECMAScript 5 Object.create() pour réaliser facilement cet héritage. Si nous créons un objet b comme ça :

var b = Object.create(a);

alors,

a.isPrototypeOf(b);// true (problem solved, you included object a in the prototype chain of object b.)

Donc, si vous faites du scripting orienté objet, alors Object.create() est très utile pour l'héritage.

0 votes

C'est donc un peu comme la création d'un objet sans invocation du constructeur ? Nous allons profiter de tous les avantages de la classe. L'obj instanceof Class sera également vrai. Mais nous n'invoquons pas la fonction Class via new.

0 votes

@Anshul Tu as dit ça a.isPrototypeOf(b); retournera false ce qui est juste, car les deux Objets sont différents et pointent vers une mémoire différente. La manière correcte de procéder avec le new L'opérateur est ici. - jsfiddle.net/167onunp .

0 votes

Pourquoi ne pas simplement définir la propriété prototype de b sur a, au lieu de faire cela ?

48voto

Leopd Points 12652

Ceci :

var foo = new Foo();

y

var foo = Object.create(Foo.prototype);

sont assez semblables. Une différence importante est que new Foo exécute réellement le code du constructeur, alors que Object.create n'exécutera pas de code tel que

function Foo() {
    alert("This constructor does not run with Object.create");
}

Notez que si vous utilisez la version à deux paramètres de l'option Object.create() alors vous pouvez faire des choses beaucoup plus puissantes.

1 votes

Excellente explication. Puis-je ajouter qu'en utilisant Object.create Dans sa forme la plus simple, cela vous permet d'omettre les fonctions de construction de votre code tout en profitant de l'héritage des prototypes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X