javascript - mdn - object.create vs new
Comprender la diferencia entre Object.create() y el nuevo SomeFunction() (10)
Recientemente me topé con el método Object.create()
en JavaScript, y estoy tratando de deducir en qué se diferencia de crear una nueva instancia de un objeto con el new SomeFunction()
, y cuándo querría usar uno sobre el otro.
Considere el siguiente ejemplo:
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
Observe que se observa el mismo comportamiento en ambos casos. Me parece que las principales diferencias entre estos dos escenarios son:
- El objeto utilizado en
Object.create()
realidad forma el prototipo del nuevo objeto, mientras que en lanew Function()
de las propiedades / funciones declaradas no forma el prototipo. - No puede crear cierres con la sintaxis
Object.create()
como lo haría con la sintaxis funcional. Esto es lógico dado el alcance de tipo léxico (bloque vs) de JavaScript.
¿Son correctas las afirmaciones anteriores? ¿Y me estoy perdiendo algo? ¿Cuándo usarías uno sobre el otro?
EDITAR: enlace a la versión jsfiddle del ejemplo de código anterior: http://jsfiddle.net/rZfYL/
El objeto utilizado en Object.create en realidad forma el prototipo del nuevo objeto, donde, al igual que en la nueva Función (), las propiedades / funciones declaradas no forman el prototipo.
Sí, Object.create
construye un objeto que hereda directamente del que pasó como primer argumento.
Con las funciones del constructor, el objeto recién creado se hereda del prototipo del constructor, por ejemplo:
var o = new SomeConstructor();
En el ejemplo anterior, o
hereda directamente de SomeConstructor.prototype
.
Hay una diferencia aquí, con Object.create
puedes crear un objeto que no se hereda de nada, Object.create(null);
, por otro lado, si establece SomeConstructor.prototype = null;
el objeto recién creado heredará de Object.prototype
.
No puede crear cierres con la sintaxis Object.create como lo haría con la sintaxis funcional. Esto es lógico dado el alcance de tipo léxico (bloque vs) de JavaScript.
Bueno, puede crear cierres, por ejemplo, usando el argumento de descriptores de propiedad:
var o = Object.create({inherited: 1}, {
foo: {
get: (function () { // a closure
var closured = ''foo'';
return function () {
return closured+''bar'';
};
})()
}
});
o.foo; // "foobar"
Tenga en cuenta que estoy hablando del método ECMAScript 5th Edition Object.create
, no de la leyenda de Crockford.
El método está comenzando a implementarse de forma nativa en los últimos navegadores, verifique esta tabla de compatibilidad .
Déjame intentar explicar (más en el Blog ):
- Cuando escribes
Car
constructorvar Car = function(){}
, así es como son las cosas internamente: Tenemos un{prototype}
enlace oculto aFunction.prototype
que no es accesible y un enlaceprototype
aCar.prototype
que es accesible y tiene unconstructor
real deCar
. Tanto Function.prototype como Car.prototype tienen enlaces ocultos aObject.prototype
. Cuando queremos crear dos objetos equivalentes utilizando el
new
operador y el método decreate
, tenemos que hacerlo así:Honda = new Car();
yMaruti = Object.create(Car.prototype)
. ¿Que esta pasando?Honda = new Car();
- Cuando creas un objeto como este, la propiedad oculta{prototype}
apunta aCar.prototype
. Así que aquí, el{prototype}
del objeto Honda siempre seráCar.prototype
- no tenemos ninguna opción para cambiar la propiedad{prototype}
del objeto. ¿Qué pasa si quiero cambiar el prototipo de nuestro objeto recién creado?
Maruti = Object.create(Car.prototype)
- Cuando crea un objeto como este, tiene una opción adicional para elegir la propiedad{prototype}
su objeto. Si desea que Car.prototype sea el{prototype}
, páselo como parámetro en la función. Si no desea ningún{prototype}
para su objeto, puede pasar unnull
como este:Maruti = Object.create(null)
.
Conclusión - Al usar el método Object.create
, tiene la libertad de elegir su propiedad {prototype}
objeto. En new Car();
, no tienes esa libertad.
Manera preferida en OO JavaScript:
Supongamos que tenemos dos objetos a
y b
.
var a = new Object();
var b = new Object();
Ahora, supongamos que a
tiene algunos métodos a los que b
también quiere acceder. Para eso, requerimos la herencia de objetos ( a
debe ser el prototipo de b
solo si queremos acceder a esos métodos). Si verificamos los prototipos de a
y b
, descubriremos que comparten el prototipo Object.prototype
.
Object.prototype.isPrototypeOf(b); //true
a.isPrototypeOf(b); //false (the problem comes into the picture here).
Problema: queremos que el objeto a
sea el prototipo de b
, pero aquí creamos el objeto b
con el prototipo Object.prototype
. Solución: ECMAScript 5 introdujo Object.create()
, para lograr tal herencia fácilmente. Si creamos el objeto b
como este:
var b = Object.create(a);
entonces,
a.isPrototypeOf(b);// true (problem solved, you included object a in the prototype chain of object b.)
Por lo tanto, si está realizando scripts orientados a objetos, Object.create()
es muy útil para la herencia.
De acuerdo con esta respuesta y este video, la new
palabra clave hace lo siguiente:
Crea nuevo objeto.
Enlaza el nuevo objeto a la función constructora (
prototype
).Hace que
this
variable apunte al nuevo objeto.Ejecuta la función de constructor usando el nuevo objeto y la ejecución implícita
return this
;Asigna el nombre de la función del constructor al nuevo
constructor
propiedades del objeto.
Object.create
realiza solo los pasos 1st
y 2nd
!
En pocas palabras, la new X
es Object.create(X.prototype)
y además ejecuta la función constructor
. (Y le da al constructor
la oportunidad de return
el objeto real que debería ser el resultado de la expresión en lugar de this
).
Eso es. :)
El resto de las respuestas son confusas, porque aparentemente nadie más lee la definición de new
tampoco. ;)
Esta:
var foo = new Foo();
y
var foo = Object.create(Foo.prototype);
Son bastante similares. Una diferencia importante es que el new Foo
realidad ejecuta el código del constructor, mientras que Object.create
no ejecutará código como el
function Foo() {
alert("This constructor does not run with Object.create");
}
Tenga en cuenta que si usa la versión de dos parámetros de Object.create()
, puede hacer cosas mucho más poderosas.
Estos son los pasos que ocurren internamente para ambas llamadas:
(Pista: la única diferencia es en el paso 3)
new Test()
:
- crear
new Object()
obj - establecer
obj.__proto__
aTest.prototype
-
return Test.call(obj) || obj; // normally obj is returned but constructors in JS can return a value
Object.create( Test.prototype )
- crear
new Object()
obj - establecer
obj.__proto__
aTest.prototype
-
return obj;
Así que básicamente Object.create
no ejecuta el constructor.
Internamente Object.create
hace esto:
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
La sintaxis simplemente quita la ilusión de que JavaScript utiliza la herencia clásica.
La diferencia es la llamada "herencia pseudoclásica versus prototípica". La sugerencia es usar solo un tipo en su código, no mezclar los dos.
En la herencia pseudoclásica (con el operador "nuevo"), imagine que primero define una pseudo-clase y luego crea objetos de esa clase. Por ejemplo, defina una pseudo-clase "Persona", y luego cree "Alice" y "Bob" a partir de "Persona".
En la herencia prototípica (usando Object.create), crea directamente una persona específica "Alice", y luego crea otra persona "Bob" usando "Alice" como prototipo. No hay "clase" aquí; todos son objetos
Internamente, JavaScript usa la "herencia prototípica"; La forma "pseudoclásica" es solo un poco de azúcar.
Vea este enlace para una comparación de las dos maneras.
Variantes de creación de objetos.
Variante 1 : '' nuevo objeto () '' -> constructor de objetos sin argumentos.
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 : '' Nuevo objeto (persona) '' -> Objeto constructor con argumento.
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 : '' Object.create (person) ''. Utilice Object.create con el simple objeto ''person''. ''Object.create (person)'' creará (y devolverá) un nuevo objeto vacío y agregará la propiedad ''__proto__'' al mismo nuevo objeto vacío. Esta propiedad ''__proto__'' apuntará al objeto ''persona''.
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) ''. Utilice Object.create con objeto incorporado -> ''Object.prototype''. ''Object.create (Object.prototype)'' creará (y devolverá) un nuevo objeto vacío y agregará la propiedad ''__proto__'' al mismo nuevo objeto vacío. Esta propiedad ''__proto__'' apuntará al objeto ''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 : '' nuevo 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!
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
Resumen:
1) con la new
palabra clave hay dos cosas a tener en cuenta;
a) la función se utiliza como un constructor
b) el objeto function.prototype
se pasa a la propiedad __proto__
... o donde __proto__
no se admite, es el segundo lugar donde el nuevo objeto busca propiedades.
2) con Object.create(obj.prototype)
está construyendo un objeto ( obj.prototype
) y pasándolo al objeto deseado ... con la diferencia de que ahora el nuevo __proto__
del objeto también apunta a obj.prototype (por favor, ref. xj9 para eso)