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:
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.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/
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é .
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. ;)
Voici les étapes à suivre en interne pour les deux appels:
(Indice: la seule différence est à l'étape 3)
new Test()
:
new Object()
objobj.__proto__
à Test.prototype
return Test.call(obj) || obj; // normally obj is returned but constructors in JS can return a value
Object.create( Test.prototype )
new Object()
objobj.__proto__
à Test.prototype
return obj;
Donc, fondamentalement, Object.create
n'exécute pas le constructeur.
Laissez-moi essayer d'expliquer (plus sur Blog ):
Car
constructeur var Car = function(){}
, voici comment les choses se passent en interne: 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
.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)
. 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.
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.
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.
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)
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.
En conséquence, le mot-clé cette réponse et le mot-clé cette vidéonew
a les effets suivants:
Crée un nouvel objet.
Lie le nouvel objet à la fonction constructeur (prototype
).
Fait que la variable this
pointe sur le nouvel objet.
Exécute la fonction constructeur à l'aide du nouvel objet et implicite effectue return this
;
Assigne le nom de la fonction constructeur à la propriété du nouvel objet constructor
.
Object.create
effectue uniquement les étapes 1st
et 2nd
!!!
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
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 }
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!
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
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!