type objects new create javascript constructor new-operator object-create

objects - this object javascript



Usando “Object.create” en lugar de “nuevo” (13)

Resumen:

  • Object.create() es una función de Javascript que toma 2 argumentos y devuelve un nuevo objeto.
  • El primer argumento es un objeto que será el prototipo del objeto recién creado.
  • El segundo argumento es un objeto que será las propiedades del objeto recién creado.

Ejemplo:

const proto = { talk : () => console.log(''hi'') } const props = { age: { writable: true, configurable: true, value: 26 } } let Person = Object.create(proto, props) console.log(Person.age); Person.talk();

Aplicaciones prácticas:

  1. La principal ventaja de crear un objeto de esta manera es que el prototipo se puede definir explícitamente . Al usar un objeto literal, o la new palabra clave, no tiene control sobre esto (sin embargo, puede sobrescribirlos, por supuesto).
  2. Si queremos tener un prototipo La new palabra clave invoca una función constructora. Con Object.create() no hay necesidad de invocar o incluso declarar una función constructora .
  3. Básicamente, puede ser una herramienta útil cuando desea crear objetos de una manera muy dinámica. Podemos hacer una función de fábrica de objetos que crea objetos con diferentes prototipos dependiendo de los argumentos recibidos.

Javascript 1.9.3 / ECMAScript 5 presenta Object.create , que Douglas Crockford, entre otros, ha advocating durante mucho tiempo. ¿Cómo reemplazo el código new a continuación con Object.create ?

var UserA = function(nameParam) { this.id = MY_GLOBAL.nextId(); this.name = nameParam; } UserA.prototype.sayHello = function() { console.log(''Hello ''+ this.name); } var bob = new UserA(''bob''); bob.sayHello();

(Supongamos que MY_GLOBAL.nextId existe).

Lo mejor que se me ocurre es:

var userB = { init: function(nameParam) { this.id = MY_GLOBAL.nextId(); this.name = nameParam; }, sayHello: function() { console.log(''Hello ''+ this.name); } }; var bob = Object.create(userB); bob.init(''Bob''); bob.sayHello();

No parece haber ninguna ventaja, así que creo que no lo estoy consiguiendo. Probablemente estoy siendo demasiado neoclásico. ¿Cómo debo usar Object.create para crear el usuario ''bob''?


A veces no puede crear un objeto con NEW pero aún puede invocar el método CREATE.

Por ejemplo: si desea definir un elemento personalizado, debe derivar de HTMLElement.

proto = new HTMLElement //fail :( proto = Object.create( HTMLElement.prototype ) //OK :) document.registerElement( "custom-element", { prototype: proto } )


Con solo un nivel de herencia, es posible que su ejemplo no le permita ver los beneficios reales de Object.create .

Este método le permite implementar fácilmente la herencia diferencial , donde los objetos pueden heredar directamente de otros objetos.

En su ejemplo de userB , no creo que su método de init deba ser público o incluso existir, si llama nuevamente a este método en una instancia de objeto existente, las propiedades de id y name cambiarán.

Object.create permite inicializar las propiedades del objeto utilizando su segundo argumento, por ejemplo:

var userB = { sayHello: function() { console.log(''Hello ''+ this.name); } }; var bob = Object.create(userB, { ''id'' : { value: MY_GLOBAL.nextId(), enumerable:true // writable:false, configurable(deletable):false by default }, ''name'': { value: ''Bob'', enumerable: true } });

Como puede ver, las propiedades se pueden inicializar en el segundo argumento de Object.create , con un objeto literal utilizando una sintaxis similar a la utilizada por los métodos Object.defineProperties y Object.defineProperty .

Te permite establecer los atributos de propiedad ( enumerable , writable o configurable ), que pueden ser realmente útiles.


Creo que el punto principal en cuestión es entender la diferencia entre los enfoques new y los de Object.create . De acuerdo con esta respuesta y este video, la new palabra clave hace lo siguiente:

  1. Crea nuevo objeto.

  2. Enlaza el nuevo objeto a la función constructora ( prototype ).

  3. Hace que this variable apunte al nuevo objeto.

  4. Ejecuta la función de constructor usando el nuevo objeto y la ejecución implícita return this ;

  5. 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 el ejemplo de código proporcionado en cuestión, no es gran cosa, pero en el siguiente ejemplo es:

var onlineUsers = []; function SiteMember(name) { this.name = name; onlineUsers.push(name); } SiteMember.prototype.getName = function() { return this.name; } function Guest(name) { SiteMember.call(this, name); } Guest.prototype = new SiteMember(); var g = new Guest(''James''); console.log(onlineUsers);

Como resultado de efectos secundarios será:

[ undefined, ''James'' ]

debido a Guest.prototype = new SiteMember();
Pero no necesitamos ejecutar el método del constructor principal, solo necesitamos que el método getName esté disponible en Guest. Por lo tanto tenemos que usar Object.create .
Si reemplaza Guest.prototype = new SiteMember();
to Guest.prototype = Object.create(SiteMember.prototype); resultado sea:

[ ''James'' ]


La ventaja es que Object.create suele ser más lento que new en la mayoría de los navegadores

En este ejemplo de jsperf , en un Chromium, el new navegador es 30 veces más rápido que Object.create(obj) aunque ambos son bastante rápidos. Todo esto es bastante extraño porque lo nuevo hace más cosas (como invocar a un constructor) donde Object.create debería estar creando un nuevo Objeto con el objeto pasado como un prototipo (enlace secreto en Crockford-habla)

Tal vez los navegadores no hayan alcanzado el Object.create hacer que Object.create más eficiente (quizás lo están basando en lo new bajo las coberturas ... incluso en el código nativo)


Object.create aún no es estándar en varios navegadores, por ejemplo, IE8, Opera v11.5, Konq 4.3 no lo tienen. Puede usar la versión de Douglas Crockford de Object.create para esos navegadores, pero esto no incluye el segundo parámetro de "objeto de inicialización" usado en la respuesta de CMS.

Para el código de navegador cruzado, una forma de obtener la inicialización del objeto mientras tanto es personalizar el Object.create de Crockford. Aquí hay un método:

Object.build = function(o) { var initArgs = Array.prototype.slice.call(arguments,1) function F() { if((typeof o.init === ''function'') && initArgs.length) { o.init.apply(this,initArgs) } } F.prototype = o return new F() }

Esto mantiene la herencia prototípica de Crockford, y también verifica cualquier método de inicio en el objeto, luego lo ejecuta con su (s) parámetro (s), como decir new man (''John'', ''Smith''). Su código se convierte en: -

MY_GLOBAL = {i: 1, nextId: function(){return this.i++}} // For example var userB = { init: function(nameParam) { this.id = MY_GLOBAL.nextId(); this.name = nameParam; }, sayHello: function() { console.log(''Hello ''+ this.name); } }; var bob = Object.build(userB, ''Bob''); // Different from your code bob.sayHello();

Así que bob hereda el método sayHello y ahora tiene propiedades propias id = 1 y name = ''Bob''. Estas propiedades son de escritura y enumerables, por supuesto. Esta es también una forma mucho más sencilla de inicializar que para ECMA Object.create, especialmente si no está preocupado por los atributos de escritura, enumerables y configurables.

Para la inicialización sin un método init, se podría usar el siguiente mod de Crockford:

Object.gen = function(o) { var makeArgs = arguments function F() { var prop, i=1, arg, val for(prop in o) { if(!o.hasOwnProperty(prop)) continue val = o[prop] arg = makeArgs[i++] if(typeof arg === ''undefined'') break this[prop] = arg } } F.prototype = o return new F() }

Esto llena las propiedades propias del usuario B, en el orden en que se definen, utilizando los parámetros Object.gen de izquierda a derecha después del parámetro usuarioB. Utiliza el bucle for (prop in o), por lo que, según los estándares de ECMA, el orden de enumeración de la propiedad no se puede garantizar de la misma manera que el orden de definición de la propiedad. Sin embargo, varios ejemplos de código probados en (4) los principales navegadores muestran que son iguales, siempre que se use el filtro hasOwnProperty, y algunas veces incluso si no.

MY_GLOBAL = {i: 1, nextId: function(){return this.i++}}; // For example var userB = { name: null, id: null, sayHello: function() { console.log(''Hello ''+ this.name); } } var bob = Object.gen(userB, ''Bob'', MY_GLOBAL.nextId());

Algo más simple, diría que Object.build, ya que userB no necesita un método init. Además, userB no es específicamente un constructor, pero parece un objeto singleton normal. Así que con este método puede construir e inicializar desde objetos normales normales.


Otro uso posible de Object.create es clonar objetos inmutables de una manera económica y efectiva .

var anObj = { a: "test", b: "jest" }; var bObj = Object.create(anObj); bObj.b = "gone"; // replace an existing (by masking prototype) bObj.c = "brand"; // add a new to demonstrate it is actually a new obj // now bObj is {a: test, b: gone, c: brand}

Notas : El fragmento de código anterior crea un clon de un objeto de origen (es decir, no es una referencia, como en cObj = aObj). Se beneficia sobre el método de propiedades de copia (ver 1 ), ya que no copia las propiedades de los miembros objeto. Más bien, crea otro objeto de destino con su conjunto de prototipos en el objeto de origen. Además, cuando las propiedades se modifican en el objeto dest, se crean "sobre la marcha", enmascarando las propiedades del prototipo (src). Esto constituye una forma rápida y efectiva de clonar objetos inmutables.

La advertencia aquí es que esto se aplica a los objetos de origen que no deben modificarse después de la creación (inmutable). Si el objeto de origen se modifica después de la creación, también se modificarán todas las propiedades desenmascaradas del clon.

Toque aquí ( http://jsfiddle.net/y5b5q/1/ ) (necesita Object.create un navegador capaz).


Podrías hacer que el método init devuelva this , y luego encadenar las llamadas, así:

var userB = { init: function(nameParam) { this.id = MY_GLOBAL.nextId(); this.name = nameParam; return this; }, sayHello: function() { console.log(''Hello ''+ this.name); } }; var bob = Object.create(userB).init(''Bob'');


Prefiero un enfoque de cierre.

Todavía utilizo new . No uso Object.create . Yo no uso this .

Sigo usando lo new porque me gusta su carácter declarativo.

Considera esto para herencia simple.

window.Quad = (function() { function Quad() { const wheels = 4; const drivingWheels = 2; function getWheelCount() { return wheels; } function getDrivingWheelCount() { return drivingWheels; } return Object.freeze({ getWheelCount, getDrivingWheelCount }); } return Object.freeze(Quad); })(); window.Car4wd = (function() { function Car4wd() { const quad = new Quad(); const spareWheels = 1; const extraDrivingWheels = 2; function getSpareWheelCount() { return spareWheels; } function getDrivingWheelCount() { return quad.getDrivingWheelCount() + extraDrivingWheels; } return Object.freeze(Object.assign({}, quad, { getSpareWheelCount, getDrivingWheelCount })); } return Object.freeze(Car4wd); })(); let myQuad = new Quad(); let myCar = new Car4wd(); console.log(myQuad.getWheelCount()); // 4 console.log(myQuad.getDrivingWheelCount()); // 2 console.log(myCar.getWheelCount()); // 4 console.log(myCar.getDrivingWheelCount()); // 4 - The overridden method is called console.log(myCar.getSpareWheelCount()); // 1

Retroalimentación animada.


Realmente no hay ninguna ventaja en el uso de Object.create(...) sobre un new object .

Quienes abogan por este método generalmente declaran ventajas bastante ambiguas: "scalability" o " más natural para JavaScript ", etc.

Sin embargo, todavía tengo que ver un ejemplo concreto que muestra que Object.create tiene alguna ventaja sobre el uso de new . Por el contrario hay problemas conocidos con él. Sam Elsamman describe lo que sucede cuando hay objetos anidados y se Object.create(...) :

var Animal = { traits: {}, } var lion = Object.create(Animal); lion.traits.legs = 4; var bird = Object.create(Animal); bird.traits.legs = 2; alert(lion.traits.legs) // shows 2!!!

Esto ocurre porque Object.create(...) aboga por una práctica en la que los datos se utilizan para crear nuevos objetos; Aquí el dato Animal convierte en parte del prototipo de lion y bird , y causa problemas cuando se comparte. Cuando se usa nuevo, la herencia prototípica es explícita:

function Animal() { this.traits = {}; } function Lion() { } Lion.prototype = new Animal(); function Bird() { } Bird.prototype = new Animal(); var lion = new Lion(); lion.traits.legs = 4; var bird = new Bird(); bird.traits.legs = 2; alert(lion.traits.legs) // now shows 4

Con respecto a los atributos de propiedad opcionales que se pasan a Object.create(...) , estos pueden agregarse usando Object.defineProperties(...) .


Si bien Douglas Crockford solía ser un entusiasta defensor de Object.create () y básicamente es la razón por la que este constructo está en JavaScript, ya no tiene esta opinión.

Dejó de usar Object.create, porque dejó de usar esta palabra clave por completo, ya que causa demasiados problemas. Por ejemplo, si no tiene cuidado, puede apuntar fácilmente al objeto global, lo que puede tener muy malas consecuencias. Y afirma que sin usar este Object.create ya no tiene sentido.

Puedes ver este video de 2014 donde habla en Nordic.js:

https://www.youtube.com/watch?v=PSGEjv3Tqo0


TL; DR:

new Computer() invocará la función del constructor Computer(){} por una vez, mientras que Object.create(Computer.prototype) no lo hará.

Todas las ventajas se basan en este punto.

Aunque por algunas razones de optimización del motor Object.create , Object.create puede ser más lento, lo que no es intuitivo.


Tienes que hacer una función personalizada Object.create() . Una que aborde las preocupaciones de Crockford y también llame a su función de inicio.

Esto funcionará:

var userBPrototype = { init: function(nameParam) { this.name = nameParam; }, sayHello: function() { console.log(''Hello ''+ this.name); } }; function UserB(name) { function F() {}; F.prototype = userBPrototype; var f = new F; f.init(name); return f; } var bob = UserB(''bob''); bob.sayHello();

Aquí UserB es como Object.create, pero ajustado a nuestras necesidades.

Si quieres, también puedes llamar al:

var bob = new UserB(''bob'');