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 lenew 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/
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é .
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. ;)
Voici les étapes qui se produisent en interne pour les deux appels:
(Astuce: la seule différence est à l'étape 3)
new Test()
:
- créer
new Object()
obj - définir
obj.__proto__
surTest.prototype
return Test.call(obj) || obj; // normally obj is returned but constructors in JS can return a value
Object.create( Test.prototype )
- créer
new Object()
obj - définir
obj.__proto__
surTest.prototype
return obj;
Donc fondamentalement Object.create
n'exécute pas le constructeur.
Laissez-moi essayer d'expliquer (plus sur Blog) :
- lorsque vous écrivez
Car
constructeurvar Car = function(){}
, Voici comment les choses sont en interne: Nous avons un{prototype}
lien caché pourFunction.prototype
, qui n'est pas accessible et unprototype
lien versCar.prototype
, qui est accessible et dispose d'une réelleconstructor
deCar
. À La Fois La Fonction.prototype et voiture.prototype ont des liens cachés versObject.prototype
. -
Lorsque nous voulons créer deux objets équivalents en utilisant l'opérateur
new
et la méthodecreate
, nous ont de le faire comme ceci:Honda = new Car();
etMaruti = Object.create(Car.prototype)
. 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 toujoursCar.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 passernull
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.
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.
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)
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.
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.
En Conséquence de cette réponse et de cette vidéo new
mot clé n'côté des choses:
Crée un nouvel objet.
Relie le nouvel objet à la fonction constructeur (
prototype
).Fait pointer la variable
this
vers le nouvel objet.Exécute la fonction constructeur à l'aide de l'objet nouveau et implicite d'effectuer
return this
;Affecte le nom de la fonction constructeur à la propriété du nouvel objet
constructor
.
Object.create
effectue uniquement 1st
et 2nd
étapes!!!
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
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 }
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!
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
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!