recorrer orientado objetos objeto lista herencia eliminar elementos elemento array agregar javascript object methods extending

orientado - recorrer array de objetos javascript



Agregar un prototipo de constructor a un objeto javascript (4)

Entonces intenté esto:

object.__proto__ = SmartObject.prototype;

...

¿Es esta una forma adecuada y aceptable de agregar el prototipo a mi objeto? ¿O es que esto rompe los patrones orientados a objetos y se considera una mala práctica y debo seguir haciendo lo mismo que hice (usando el constructor)?

Recomiendo en contra, porque:

  1. Cambiar el prototipo de un objeto después del hecho arruina su rendimiento en los motores de JavaScript actuales.

  2. Es inusual y, por lo tanto, hace que su código sea un poco extraño para cualquier otra persona que pueda tener.

  3. Es específico del navegador; la propiedad __proto__ solo se define en un Apéndice a la especificación de JavaScript, y solo para los navegadores (aunque la especificación requiere que los navegadores la implementen). La forma no específica del navegador es Object.setPrototypeOf(object, SmartObject.prototype); pero vea # 1 y # 2.

Le preocupa que sea redundante o repetitivo, ya sea a nivel de codificación o a nivel de memoria (no estoy seguro). No lo es si abrazas tu SmartObject desde el principio en lugar de crear primero el object y luego agregar la inteligencia más adelante:

var SmartObject = function(name, description, properties) { this.name = name; this.description = description; this.properties = properties; }; SmartObject.prototype.getName = function(){ return this.name; }; SmartObject.prototype.getDescription = function(){ return this.description; }; SmartObject.prototype.getProperies = function(){ return this.properties; }; var object = new SmartObject( "object name", "object description", [ { name: "first", value: "1" }, { name: "second", value: "2" }, { name: "third", value: "3" } ] ); var anotherObject = new SmartObject( /*...*/ ); var yetAnotherObject = new SmartObject( /*...*/ );

O mejor aún, con ES2015 (que puede usar hoy en día con un transpiler como Babel):

class SmartObject { constructor() { this.name = name; this.description = description; this.properties = properties; } getName() { return this.name; } getDescription() { return this.description; } getProperies(){ return this.properties; } } let object = new SmartObject( "object name", "object description", [ { name: "first", value: "1" }, { name: "second", value: "2" }, { name: "third", value: "3" } ] ); let anotherObject = new SmartObject( /*...*/ ); let yetAnotherObject = new SmartObject( /*...*/ );

Has dicho que no puedes abrazar SmartObject desde el principio porque provienen de una fuente JSON. En ese caso, puede incorporar su SmartObject en el análisis JSON usando una función reviver :

var objects = JSON.parse(json, function(k, v) { if (typeof v === "object" && v.name && v.description && v.properties) { v = new SmartObject(v.name, v.description, v.properties); } return v; });

Si bien eso significa que los objetos se crean primero y luego se vuelven a crear, crear objetos es una operación muy barata; Aquí hay un ejemplo que muestra la diferencia de tiempo al analizar 20k objetos con y sin un revividor:

var json = ''[''; for (var n = 0; n < 20000; ++n) { if (n > 0) { json += '',''; } json += ''{'' + '' "name": "obj'' + n + ''",'' + '' "description": "Object '' + n + ''",'' + '' "properties": ['' + '' {'' + '' "name": "first",'' + '' "value": "'' + Math.random() + ''"'' + '' },'' + '' {'' + '' "name": "second",'' + '' "value": "'' + Math.random() + ''"'' + '' }'' + '' ]'' + ''}''; } json += '']''; var SmartObject = function(name, description, properties) { this.name = name; this.description = description; this.properties = properties; }; SmartObject.prototype.getName = function() { return this.name; }; SmartObject.prototype.getDescription = function() { return this.description; }; SmartObject.prototype.getProperies = function() { return this.properties; }; console.time("parse without reviver"); console.log("count:", JSON.parse(json).length); console.timeEnd("parse without reviver"); console.time("parse with reviver"); var objects = JSON.parse(json, function(k, v) { if (typeof v === "object" && v.name && v.description && v.properties) { v = new SmartObject(v.name, v.description, v.properties); } return v; }); console.log("count:", objects.length); console.timeEnd("parse with reviver"); console.log("Name of first:", objects[0].getName());

En mi máquina, duplica aproximadamente el tiempo, pero estamos hablando de ~ 60 ms a ~ 120 ms, por lo que en términos absolutos no hay nada de qué preocuparse, y eso es para objetos de 20.000.

Alternativamente, puedes mezclar tus métodos en lugar de tener un prototipo:

// The methods to mix in var smartObjectMethods = { getName() { return this.name; }, getDescription() { return this.description; }, getProperies() { return this.properties; } }; // Remember their names to make it faster adding them later var smartObjectMethodNames = Object.keys(smartObjectMethods); // Once we have the options, we update them all: objects.forEach(function(v) { smartObjectMethodNames.forEach(function(name) { v[name] = smartObjectMethods[name]; }); });

ES2015 tiene Object.assign que puede usar en lugar de smartObjectMethodNames y el forEach interno:

// Once we have the options, we update them all: objects.forEach(function(v) { Object.assign(v, smartObjectMethods); });

De cualquier manera, es un poco menos eficiente en memoria porque cada uno de los objetos termina teniendo sus propias propiedades getName , getDescription y getProperties (las funciones no se duplican, se comparten, pero las propiedades para referirse a ellas se duplican). Sin embargo, eso es extremadamente improbable que sea un problema.

Aquí hay un ejemplo con 20k objetos de nuevo:

var json = ''[''; for (var n = 0; n < 20000; ++n) { if (n > 0) { json += '',''; } json += ''{'' + '' "name": "obj'' + n + ''",'' + '' "description": "Object '' + n + ''",'' + '' "properties": ['' + '' {'' + '' "name": "first",'' + '' "value": "'' + Math.random() + ''"'' + '' },'' + '' {'' + '' "name": "second",'' + '' "value": "'' + Math.random() + ''"'' + '' }'' + '' ]'' + ''}''; } json += '']''; var smartObjectMethods = { getName() { return this.name; }, getDescription() { return this.description; }, getProperies() { return this.properties; } }; var smartObjectMethodNames = Object.keys(smartObjectMethods); console.time("without adding methods"); console.log("count:", JSON.parse(json).length); console.timeEnd("without adding methods"); console.time("with adding methods"); var objects = JSON.parse(json); objects.forEach(function(v) { smartObjectMethodNames.forEach(function(name) { v[name] = smartObjectMethods[name]; }); }); console.log("count:", objects.length); console.timeEnd("with adding methods"); if (Object.assign) { // browser has it console.time("with assign"); var objects = JSON.parse(json); objects.forEach(function(v) { Object.assign(v, smartObjectMethods); }); console.log("count:", objects.length); console.timeEnd("with assign"); } console.log("Name of first:", objects[0].getName());

Tengo varios objetos javascript como este:

var object = { name: "object name", description: "object description", properties: [ { name: "first", value: "1" }, { name: "second", value: "2" }, { name: "third", value: "3" } ] };

Ahora quería cambiar estos objetos a objetos más inteligentes (agregar algunos métodos, etc.).
Al principio hice una función constructora como esta:

SmartObject = function( object ){ this.name = object.name; this.description = object.description; this.properties = object.properties; }; SmartObject.prototype.getName = function(){ return this.name; }; SmartObject.prototype.getDescription = function(){ return this.description; }; SmartObject.prototype.getProperies = function(){ return this.properties; };

Y luego utilicé este constructor para cambiar mi objeto a una instancia de SmartObject como esta:

var smartObject = new SmartObject( object );

Este parece ser el código javascript orientado a objetos adecuado para hacer esto, pero se siente muy complicado ya que lo único que quiero hacer es agregar algunos métodos y ahora copio todas las propiedades de mi object a mi SmartObject en la función de constructor.

En este ejemplo, solo hay 3 propiedades y algunos métodos simples, pero en mi proyecto hay varias docenas de propiedades (anidadas) y métodos mucho más complejos.

Entonces intenté esto:

object.__proto__ = SmartObject.prototype;

Y esto parece dar exactamente el mismo resultado y parece mucho más fácil (ver este violín para este ejemplo).

¿Es esta una forma adecuada y aceptable de agregar el prototipo a mi objeto? ¿O es que esto rompe los patrones orientados a objetos y se considera una mala práctica y debo seguir haciendo lo mismo que hice (usando el constructor)?

¿O tal vez hay otra forma más aceptable de agregar los métodos a mi objeto existente sin pasar por la función de constructor?

Nota. Traté de encontrar ese ejemplo en StackOverflow, pero cuando busco, siempre termino en ejemplos que extienden el prototipo de las clases de JavaScript existentes. Si hay tal pregunta, siéntase libre de marcar esto como un duplicado y cerraré mi pregunta nuevamente.


Combinando el Object.create() de @SebastienDaniel su respuesta y @bloodyKnuckles su comentario y el método Object.assign() sugerido por @nnnnnn en su comentario, logré con el siguiente código simple hacer exactamente lo que quería:

var smartObject = Object.assign( Object.create( SmartObject.prototype ), object );

Consulta el violín actualizado aquí.


Como mencioné anteriormente, cambiar el prototipo de un objeto tendrá un impacto severo en el rendimiento de su código. ( tbh, nunca me he tomado el tiempo para medir el impacto ). Esta página de MDN explica .

Sin embargo, si su problema está relacionado con la plantilla, puede crear fácilmente una fábrica genérica para sus objetos, como:

function genericFactory(proto, source) { return Object.keys(source).reduce(function(target, key) { target[key] = source[key]; return target; }, Object.create(proto)); }

Y ahora puede usarlo pasando su SmartObject.prototype y el object como argumentos como este:

var smartObject = genericFactory(SmartObject.prototype, object);


Dado que su objeto es el mismo que SmartObject en las propiedades, puede encontrar cosas como esta;

var obj = { name: "object name", description: "object description", properties: [ { name: "first", value: "1" }, { name: "second", value: "2" }, { name: "third", value: "3" } ] }, SmartObject = function( object ){ this.name = object.name; this.description = object.description; this.properties = object.properties; }; SmartObject.prototype.getName = function(){ return this.name; }; SmartObject.prototype.getDescription = function(){ return this.description; }; SmartObject.prototype.getProperties = function(){ return this.properties; }; obj.constructor = SmartObject; obj.__proto__ = obj.constructor.prototype; console.log(obj.getName());

Ok, la propiedad __proto__ ahora está incluida en el estándar ECMAScript y es segura de usar. Sin embargo, también existe el método de objeto Object.setPrototypeOf() que se puede utilizar de la misma manera. Por lo tanto, también puede hacer como Object.setPrototypeOf(obj, obj.constructor.prototype) en lugar de obj.__proto__ = obj.constructor.prototype;