La compréhension de la différence entre l'Objet.créer() et le nouveau SomeFunction()

Je suis récemment tombé sur l' Object.create() méthode en JavaScript, et j'essaie d'en déduire comment il est différent de la création d'une nouvelle instance d'un objet avec new SomeFunction(), et lorsque vous souhaitez utiliser l'un 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:

  • l'objet utilisé dans Object.create() forme en fait le prototype du nouvel objet, alors que dans le new Function() des propriétés/fonctions déclarées ne forment 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 de type lexical (vs block) de JavaScript.

Les énoncés ci-dessus sont-ils corrects? Et ai-je raté 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/

335
demandé sur BanksySan 2010-11-12 19:12:39

10 réponses

L'objet utilisé dans Object.create forme réellement le prototype du nouvel objet, où, comme dans la nouvelle fonction (), 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 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);, 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 l'objet.créez la syntaxe comme vous le feriez avec la syntaxe fonctionnelle. Ceci est logique étant donné la portée de type lexical (vs block) de JavaScript.

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

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 5ème édition ECMAScript Object.create Méthode, pas la cale de Crockford.

La méthode commence à être implémentée nativement sur les derniers navigateurs, vérifiez ceci table de compatibilité .

218
répondu CMS 2010-11-12 16:22:29

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

C'est ça. :)

Le reste des réponses est juste déroutant, car apparemment personne d'autre ne lit la définition de new non plus. ;)

341
répondu Evi1M4chine 2018-01-27 19:09:34

Voici les étapes qui se produisent en interne pour les deux appels:
(Astuce: la seule différence est à l'étape 3)


new Test():

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

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

184
répondu Ray Hulha 2017-02-13 12:40:53

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

  1. lorsque vous écrivez Car constructeur var Car = function(){}, Voici comment les choses sont en interne: Un diagramme de chaînes prototypiques lors de la création d'objets javascript Nous avons un {prototype} lien caché pour Function.prototype, qui n'est pas accessible et un prototype lien vers Car.prototype, qui est accessible et dispose d'une réelle constructor de Car. À La Fois La Fonction.prototype et voiture.prototype ont des liens cachés vers Object.prototype.
  2. Lorsque nous voulons créer deux objets équivalents en utilisant l'opérateur new et la méthode create, nous ont de le faire comme ceci: Honda = new Car(); et Maruti = Object.create(Car.prototype).Un diagramme de chaînes prototypiques pour différentes méthodes de création d'objets Ce qui se passe?

    Honda = new Car(); - Lorsque vous créez un objet comme celui-ci caché {prototype} la propriété est pointé 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. Que faire si je veux changer le prototype de notre objet nouvellement créé?
    Maruti = Object.create(Car.prototype) - Lorsque vous créez un objet comme cela, vous disposez d'une option supplémentaire pour choisir votre objet {prototype} propriété. Si vous voulez la Voiture.prototype comme {prototype} puis passez-le en tant que paramètre dans la fonction. Si vous ne voulez pas de {prototype} pour votre objet, puis 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 objet {prototype} la propriété. 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();

Maintenant, supposons que a ait des méthodes auxquelles b veut également accéder. Pour cela, nous avons besoin d'héritage d'objet (a devrait être le prototype de b seulement si nous voulons accéder à ces méthodes). Si nous vérifions les prototypes de a et b, puis on va le savoir 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 objet a, comme le prototype de b, mais ici nous avons créé un objet b avec le prototype Object.prototype. Solution- ECMAScript 5 introduit Object.create(), pour obtenir un tel héritage facilement. Si nous créons l'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 faites des scripts orientés objet, Object.create() est très utile pour l'héritage.

57
répondu Anshul 2017-03-18 03:39:44

Ceci:

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 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 Object.create(), vous pouvez faire des choses beaucoup plus puissantes.

35
répondu Leopd 2014-04-21 19:27:59
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

Résumé:

1) new clé il y a deux choses à noter;

A) la fonction est utilisée comme constructeur

B) l'objet function.prototype est passé à la propriété __proto__... ou lorsque __proto__ n'est pas pris en charge, c'est le deuxième endroit où le nouvel objet cherche à trouver des propriétés

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

20
répondu user3124360 2015-07-22 11:34:37

La différence est la soi-disant "pseudo-Classique vs héritage prototypique". La suggestion est d'utiliser un seul type dans votre code, sans mélanger les deux.

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

Dans l'héritage prototypique (en utilisant Object.créer de), vous créer directement un spécifique personne "Alice", puis créer 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 prototypique"; la manière "pseudoclassique" est juste un peu de sucre.

Voir ce lien pour une comparaison des deux façons.

19
répondu user1931858 2013-06-22 00:28:32

En interne Object.create fait ceci:

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

La syntaxe enlève simplement l'illusion que JavaScript utilise L'héritage classique.

10
répondu xj9 2010-11-12 16:19:13

En Conséquence de cette réponse et de cette vidéo new mot clé n'côté des choses:

  1. Crée un nouvel objet.

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

  3. Fait pointer la variable this vers le nouvel objet.

  4. Exécute la fonction constructeur à l'aide de l'objet nouveau et implicite d'effectuer return this;

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

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

8
répondu Vladimir Kovpak 2017-07-27 19:55:01

Variantes de création D'objets.


Variante 1 : 'nouvel Objet()' -> Objet constructeur 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

entrez la description de l'image ici


Variante 2 : 'nouvel Objet(personne)' -> constructeur de l'Objet avec l'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 }

entrez la description de l'image ici


Variante 3.1 : 'Objet.créer(personne)'. Utiliser L'Objet.créer avec un objet simple 'personne'. 'Objet.créer (personne)' va créer (et return) nouvel objet vide et ajouter la propriété '_ _ proto__ ' au même nouvel objet vide. Cette propriété '_ _ proto__ 'pointera vers l'objet 'person'.

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!

entrez la description de l'image ici


Variante 3.2 : 'Objet.créer(de l'Objet.prototype) ". Utiliser L'Objet.créer avec construit-dans l'objet -> 'Objet.prototype". 'Objet.créer(de l'Objet.prototype) 'va créer (et retourner) un nouvel objet vide et ajouter la propriété' _ _ proto__' au même nouvel objet vide. Cette propriété__proto__ " sera de pointez sur l'objet " Objet.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

entrez la description de l'image ici


Variante 4 : 'nouveau 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!

entrez la description de l'image ici

5
répondu Ted 2018-03-24 13:56:09