web-dev-qa-db-fra.com

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

Je suis récemment tombé sur la méthode Object.create() en JavaScript, et j'essaie de déduire en quoi elle diffère de la création d'une nouvelle instance d'un objet avec new SomeFunction() et du moment où vous voudriez utiliser l'une sur 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

Notez 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 les suivantes:

  • L'objet utilisé dans Object.create() forme en réalité le prototype du nouvel objet, alors que dans la new Function() à partir des propriétés/fonctions déclarées, il ne constitue pas le 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 du type lexical (vs block) de JavaScript.

Les déclarations ci-dessus sont-elles correctes? Et est-ce que je manque quelque chose? Quand utiliseriez-vous l'un sur l'autre?

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

365
Matt

L'objet utilisé dans Object.create constitue en fait le prototype du nouvel objet, alors que, comme dans le nouveau formulaire Function (), les propriétés/fonctions déclarées ne constituent pas le prototype.

Oui, _Object.create_ construit un objet qui hérite directement de celui transmis comme premier argument.

Avec les fonctions constructeur, l’objet nouvellement créé hérite du prototype du constructeur, par exemple:

_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);, par contre, si vous définissez _SomeConstructor.prototype = null;_ le nouvel objet 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 du type lexical (vs block) de JavaScript.

Eh bien, vous pouvez créer des fermetures, par exemple en utilisant l'argument descripteur 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 la méthode ECMAScript 5th Edition Object.create , pas de la cale de Crockford.

La méthode commence à être implémentée de manière native sur les derniers navigateurs. Cochez table de compatibilité .

235
CMS

Très simplement, new X est Object.create(X.prototype) avec également la fonction constructor. (Et en donnant à la constructor la chance de return l'objet qui devrait être le résultat de l'expression au lieu de this.)

C'est ça. :)

Les autres réponses sont simplement déroutantes, car apparemment personne d'autre ne lit la définition de new non plus. ;)

387
Evi1M4chine

Voici les étapes à suivre en interne pour les deux appels:
(Indice: la seule différence est à l'étape 3)


new Test():

  1. créer new Object() obj
  2. mettre obj.__proto__ à 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. mettre obj.__proto__ à Test.prototype
  3. return obj;

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

197
Ray Hulha

Laissez-moi essayer d'expliquer (plus sur Blog ):

  1. Lorsque vous écrivez Car constructeur var Car = function(){}, voici comment 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 a un constructor réel de Car. Function.prototype et Car.prototype ont des liens masqués vers Object.prototype.
  2. Lorsque nous voulons créer deux objets équivalents à l'aide de l'opérateur new et de la méthode create, nous devons procéder comme suit: Honda = new Car(); et Maruti = Object.create(Car.prototype).A diagram of prototypal chains for differing object creation methods Qu'est-ce qui se passe?

    Honda = new Car(); - Lorsque vous créez un objet comme celui-ci, la propriété hidden {prototype} est alors dirigée vers Car.prototype. Donc ici, le {prototype} de l'objet Honda sera toujours Car.prototype - nous n'avons aucune option pour changer la propriété {prototype} de l'objet. Et si je veux changer le prototype de notre objet nouvellement créé?
    Maruti = Object.create(Car.prototype) - Lorsque vous créez un objet comme celui-ci, vous disposez d'une option supplémentaire pour choisir la propriété {prototype} de votre objet. Si vous voulez Car.prototype en tant que {prototype}, transmettez-le en tant que paramètre dans la fonction. Si vous ne voulez pas de {prototype} pour votre objet, vous pouvez passer null comme ceci: Maruti = Object.create(null).

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

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

Supposons que nous ayons deux objets a et b.

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

Supposons maintenant que a dispose de méthodes auxquelles b veut également accéder. Pour cela, nous avons besoin de l'héritage d'objet (a ne devrait être le prototype de b que si nous voulons accéder à ces méthodes). Si nous vérifions les prototypes de a et b, nous verrons 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 prototype de b, mais ici nous avons créé l'objet b avec le prototype Object.prototype . Solution - ECMAScript 5 a introduit Object.create(), pour réaliser facilement cet héritage. Si nous créons un objet b comme ceci:

var b = Object.create(a);

ensuite,

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

Donc, si vous utilisez un script orienté objet, alors Object.create() est très utile pour l'héritage.

59
Anshul

Cette:

var foo = new Foo();

et

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

sont assez similaires. Une différence importante est que new Foo exécute réellement le code 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 Object.create(), vous pourrez alors faire des choses beaucoup plus puissantes.

42
Leopd

La différence réside dans ce que l'on appelle "l'héritage pseudoclassique versus prototypique". La suggestion est d'utiliser un seul type dans votre code, sans les mélanger.

Dans l'héritage pseudoclassique (avec l'opérateur "new"), imaginez que vous définissiez d'abord une pseudo-classe, puis créez des objets à partir de cette classe. Par exemple, définissez une pseudo-classe "Personne", puis créez "Alice" et "Bob" à partir de "Personne".

Dans l'héritage prototype (à l'aide de Object.create), vous créez directement une personne spécifique "Alice", puis créez une autre personne "Bob" en utilisant "Alice" comme prototype. Il n'y a pas de "classe" ici; tous sont des objets.

En interne, JavaScript utilise "l'héritage prototype"; la voie "pseudoclassique" n'est que du sucre.

Voir ce lien pour une comparaison des deux manières.

22
user1931858
function Test(){
    this.prop1 = 'prop1';
    this.prop2 = 'prop2';
    this.func1 = function(){
        return this.prop1 + this.prop2;
    }
};

Test.prototype.protoProp1 = 'protoProp1';
Test.prototype.protoProp2 = 'protoProp2';
var newKeywordTest = new Test();
var objectCreateTest = Object.create(Test.prototype);

/* Object.create   */
console.log(objectCreateTest.prop1); // undefined
console.log(objectCreateTest.protoProp1); // protoProp1 
console.log(objectCreateTest.__proto__.protoProp1); // protoProp1

/* new    */
console.log(newKeywordTest.prop1); // prop1
console.log(newKeywordTest.__proto__.protoProp1); // protoProp1

Sommaire:

1) avec le mot clé new, il y a deux choses à noter;

a) la fonction est utilisée en tant que constructeur

b) L'objet function.prototype est passé à la propriété __proto__ ... ou lorsque __proto__ n'est pas pris en charge, il s'agit du deuxième endroit où le nouvel objet cherche des propriétés.

2) avec Object.create(obj.prototype) vous construisez un objet (obj.prototype) et le transmettez à l'objet souhaité .. avec la différence que le nouvel objet __proto__ pointe également sur obj.prototype (veuillez ref ans par xj9 pour cela)

21
user3124360

En interne, Object.create fait ceci:

Object.create = function (o) {
    function F() {}
    F.prototype = o;
    return new F();
};

La syntaxe supprime simplement l'illusion que JavaScript utilise l'héritage classique.

10
xj9

En conséquence, le mot-clé cette réponse et le mot-clé cette vidéonew a les effets suivants:

  1. Crée un nouvel objet.

  2. Lie le nouvel objet à la fonction constructeur (prototype).

  3. Fait que la variable this pointe sur le nouvel objet.

  4. Exécute la fonction constructeur à l'aide du nouvel objet et implicite effectue return this;

  5. Assigne le nom de la fonction constructeur à la propriété du nouvel objet constructor.

Object.create effectue uniquement les étapes 1st et 2nd !!!

9
Vladimir Kovpak

Variantes de création d'objet.


Variante 1: ' new Object () ' -> Constructeur d'objet sans arguments.

var p1 = new Object(); // 'new Object()' create and return empty object -> {}

var p2 = new Object(); // 'new Object()' create and return empty object -> {}

console.log(p1); // empty object -> {}

console.log(p2); // empty object -> {}

// p1 and p2 are pointers to different objects
console.log(p1 === p2); // false

console.log(p1.prototype); // undefined

// empty object which is in fact Object.prototype
console.log(p1.__proto__); // {}

// empty object to which p1.__proto__ points
console.log(Object.prototype); // {}

console.log(p1.__proto__ === Object.prototype); // true

// null, which is in fact Object.prototype.__proto__
console.log(p1.__proto__.__proto__); // null

console.log(Object.prototype.__proto__); // null

enter image description here


Variante 2: ' nouvel objet (personne) ' -> Constructeur d'objet avec argument.

const person = {
    name: 'no name',
    lastName: 'no lastName',
    age: -1
}

// 'new Object(person)' return 'person', which is pointer to the object ->
//  -> { name: 'no name', lastName: 'no lastName', age: -1 }
var p1 = new Object(person);

// 'new Object(person)' return 'person', which is pointer to the object ->
//  -> { name: 'no name', lastName: 'no lastName', age: -1 }
var p2 = new Object(person);

// person, p1 and p2 are pointers to the same object
console.log(p1 === p2); // true
console.log(p1 === person); // true
console.log(p2 === person); // true

p1.name = 'John'; // change 'name' by 'p1'
p2.lastName = 'Doe'; // change 'lastName' by 'p2'
person.age = 25; // change 'age' by 'person'

// when print 'p1', 'p2' and 'person', it's the same result,
// because the object they points is the same
console.log(p1); // { name: 'John', lastName: 'Doe', age: 25 }
console.log(p2); // { name: 'John', lastName: 'Doe', age: 25 }
console.log(person); // { name: 'John', lastName: 'Doe', age: 25 }

enter image description here


Variante 3.1 : ' Objet.créer (personne) '. Utilisez Object.create avec un objet simple 'personne'. 'Object.create (person)' créera (et retournera) un nouvel objet vide et ajoutera la propriété '__proto__' au même nouvel objet vide. Cette propriété '__proto__' pointera sur l'objet 'personne'.

const person = {
        name: 'no name',
        lastName: 'no lastName',
        age: -1,
        getInfo: function getName() {
           return `${this.name} ${this.lastName}, ${this.age}!`;
    }
}

var p1 = Object.create(person);

var p2 = Object.create(person);

// 'p1.__proto__' and 'p2.__proto__' points to
// the same object -> 'person'
// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(p1.__proto__);
console.log(p2.__proto__);
console.log(p1.__proto__ === p2.__proto__); // true

console.log(person.__proto__); // {}(which is the Object.prototype)

// 'person', 'p1' and 'p2' are different
console.log(p1 === person); // false
console.log(p1 === p2); // false
console.log(p2 === person); // false

// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(person);

console.log(p1); // empty object - {}

console.log(p2); // empty object - {}

// add properties to object 'p1'
// (properties with the same names like in object 'person')
p1.name = 'John';
p1.lastName = 'Doe';
p1.age = 25;

// add properties to object 'p2'
// (properties with the same names like in object 'person')
p2.name = 'Tom';
p2.lastName = 'Harrison';
p2.age = 38;

// { name: 'no name', lastName: 'no lastName', age: -1, getInfo: [Function: getName] }
console.log(person);

// { name: 'John', lastName: 'Doe', age: 25 }
console.log(p1);

// { name: 'Tom', lastName: 'Harrison', age: 38 }
console.log(p2);

// use by '__proto__'(link from 'p1' to 'person'),
// person's function 'getInfo'
console.log(p1.getInfo()); // John Doe, 25!

// use by '__proto__'(link from 'p2' to 'person'),
// person's function 'getInfo'
console.log(p2.getInfo()); // Tom Harrison, 38!

enter image description here


Variante 3.2 : ' Object.create (Object.prototype) '. Utilisez Object.create avec un objet intégré -> 'Object.prototype'. 'Object.create (Object.prototype)' créera (et retournera) un nouvel objet vide et ajoutera la propriété '__proto__' au même nouvel objet vide. Cette propriété '__proto__' désignera l'objet 'Object.prototype'.

// 'Object.create(Object.prototype)' :
// 1. create and return empty object -> {}.
// 2. add to 'p1' property '__proto__', which is link to 'Object.prototype'
var p1 = Object.create(Object.prototype);

// 'Object.create(Object.prototype)' :
// 1. create and return empty object -> {}.
// 2. add to 'p2' property '__proto__', which is link to 'Object.prototype'
var p2 = Object.create(Object.prototype);

console.log(p1); // {}

console.log(p2); // {}

console.log(p1 === p2); // false

console.log(p1.prototype); // undefined

console.log(p2.prototype); // undefined

console.log(p1.__proto__ === Object.prototype); // true

console.log(p2.__proto__ === Object.prototype); // true

enter image description here


Variante 4: ' new SomeFunction () '

// 'this' in constructor-function 'Person'
// represents a new instace,
// that will be created by 'new Person(...)'
// and returned implicitly
function Person(name, lastName, age) {

    this.name = name;
    this.lastName = lastName;
    this.age = age;

    //-----------------------------------------------------------------
    // !--- only for demonstration ---
    // if add function 'getInfo' into
    // constructor-function 'Person',
    // then all instances will have a copy of the function 'getInfo'!
    //
    // this.getInfo: function getInfo() {
    //  return this.name + " " + this.lastName + ", " + this.age + "!";
    // }
    //-----------------------------------------------------------------
}

// 'Person.prototype' is an empty object
// (before add function 'getInfo')
console.log(Person.prototype); // Person {}

// With 'getInfo' added to 'Person.prototype',
// instances by their properties '__proto__',
// will have access to the function 'getInfo'.
// With this approach, instances not need
// a copy of the function 'getInfo' for every instance.
Person.prototype.getInfo = function getInfo() {
    return this.name + " " + this.lastName + ", " + this.age + "!";
}

// after function 'getInfo' is added to 'Person.prototype'
console.log(Person.prototype); // Person { getInfo: [Function: getInfo] }

// create instance 'p1'
var p1 = new Person('John', 'Doe', 25);

// create instance 'p2'
var p2 = new Person('Tom', 'Harrison', 38);

// Person { name: 'John', lastName: 'Doe', age: 25 }
console.log(p1);

// Person { name: 'Tom', lastName: 'Harrison', age: 38 }
console.log(p2);

// 'p1.__proto__' points to 'Person.prototype'
console.log(p1.__proto__); // Person { getInfo: [Function: getInfo] }

// 'p2.__proto__' points to 'Person.prototype'
console.log(p2.__proto__); // Person { getInfo: [Function: getInfo] }

console.log(p1.__proto__ === p2.__proto__); // true

// 'p1' and 'p2' points to different objects(instaces of 'Person')
console.log(p1 === p2); // false

// 'p1' by its property '__proto__' reaches 'Person.prototype.getInfo' 
// and use 'getInfo' with 'p1'-instance's data
console.log(p1.getInfo()); // John Doe, 25!

// 'p2' by its property '__proto__' reaches 'Person.prototype.getInfo' 
// and use 'getInfo' with 'p2'-instance's data
console.log(p2.getInfo()); // Tom Harrison, 38!

enter image description here

8
Ted