javascript dynamic-languages

¿Construcción dinámica de objetos en javascript?



dynamic-languages (4)

Cuando quiero llamar a una función en javascript con argumentos proporcionados desde otro lugar, puedo usar el método de apply de la función como:

array = ["arg1", 5, "arg3"] ... someFunc.apply(null, array);

pero ¿qué pasa si necesito llamar a un constructor de una manera similar? Parece que esto no funciona:

array = ["arg1", 5, "arg3"] ... someConstructor.apply({}, array);

al menos no como estoy intentando

template = [''string1'', string2, ''etc'']; var resultTpl = Ext.XTemplate.apply({}, template);

esto no funciona marchito

Ext.XTemplate.prototype.constructor.apply({}, template);

¿Alguna forma de hacer que uno funcione? (En este caso particular, encontré que funcionará la new Ext.XTemplate(template) , pero estoy interesado en el caso general)

Pregunta similar pero específica para los tipos incorporados y sin una respuesta que puedo usar: Crear una instancia de un objeto de JavaScript llamando a prototype.constructor.apply

Gracias.

Editar:

El tiempo ha pasado y ES6 y los transpilers ahora son una cosa. En ES6 es trivial hacer lo que quería: new someConstructor(...array) . Babel lo convertirá en ES5 new (Function.prototype.bind.apply(someConstructor, [null].concat(array)))(); ¿Qué se explica en Cómo construir un objeto JavaScript (usando ''aplicar'')? .


Dado que la pregunta original es bastante antigua, aquí hay métodos más simples en prácticamente cualquier navegador moderno (desde la escritura, 2018, estoy contando Chrome, FF, IE11, Edge ...).

var template = [''string1'', string2, ''etc'']; var resultTpl = Object.create(Ext.XTemplate.prototype); Ext.XTemplate.apply(resultTpl, template);

Esas dos líneas también explican cómo funciona básicamente el new operador.


Desde developer.mozilla : Las funciones enlazadas se adaptan automáticamente al uso con el nuevo operador para construir nuevas instancias creadas por la función objetivo. Cuando se usa una función enlazada para construir un valor, siempre que esto se ignore. Sin embargo, los argumentos proporcionados todavía se añaden a la llamada del constructor.

Dicho esto, todavía tenemos que usar aplicar para obtener los argumentos de una matriz y entrar en la llamada de enlace. Además, también debemos restablecer la función de enlace como el argumento de este de la función de aplicación. Esto nos da una línea muy breve que hace exactamente lo que se necesita.

function constructorApply(ctor, args){ return new (ctor.bind.apply(ctor, [null].concat(args)))(); };


No hay una manera simple y directa de hacer esto con una función de constructor. Esto se debe a que ocurren cosas especiales cuando se usa la new palabra clave para llamar a una función de constructor, por lo que si no va a hacer eso, tiene que emular todas esas cosas especiales. Son:

  1. Creando una nueva instancia de objeto (lo estás haciendo).
  2. Configuración del prototipo interno de ese objeto en la propiedad prototype la función constructora.
  3. Estableciendo la propiedad del constructor ese objeto.
  4. Llamar a la función constructora con esa instancia de objeto como el valor de this (lo está haciendo).
  5. Manejo del valor de retorno especial de la función constructora.

Creo que eso es todo, pero vale la pena volver a comprobar en la especificación .

Así que si puedes evitarlo y simplemente usar la función de constructor directamente, lo haría. :-) Sin embargo, si no puedes, puedes hacerlo, es incómodo e implica soluciones. (Vea también esta respuesta relacionada aquí en , aunque también cubro todo el terreno [y luego algunos]).

Su mayor problema es el # 2 arriba: Configuración del prototipo interno del objeto. Durante mucho tiempo, no había una forma estándar de hacer esto. Algunos navegadores __proto__ una propiedad __proto__ que lo hizo, así que puedes usarlo si está allí. La buena noticia es que ECMAScript 5 introduce una forma de hacerlo explícitamente: Object.create . Así que los navegadores de vanguardia como Chrome tendrán eso. Pero si estás tratando con un navegador que no tiene Object.create ni __proto__ , se pone un poco feo:

1) Definir una función constructora personalizada.

2) Establezca su propiedad prototype en la propiedad prototype de la función de constructor real

3) Úsalo para crear una instancia de objeto en blanco.

Que maneja el prototipo por ti. Entonces continúas con:

4) Reemplace la propiedad del constructor en esa instancia con la función de constructor real.

5) Llame a la función de constructor real a través de apply .

6) Si el valor de retorno de la función constructora real es un objeto, utilícelo en lugar del que creó; de lo contrario, usa el que creaste.

Algo como esto ( ejemplo en vivo ):

function applyConstruct(ctor, params) { var obj, newobj; // Use a fake constructor function with the target constructor''s // `prototype` property to create the object with the right prototype function fakeCtor() { } fakeCtor.prototype = ctor.prototype; obj = new fakeCtor(); // Set the object''s `constructor` obj.constructor = ctor; // Call the constructor function newobj = ctor.apply(obj, params); // Use the returned object if there is one. // Note that we handle the funky edge case of the `Function` constructor, // thanks to Mike''s comment below. Double-checked the spec, that should be // the lot. if (newobj !== null && (typeof newobj === "object" || typeof newobj === "function") ) { obj = newobj; } // Done return obj; }

Podría Object.create un paso más allá y solo usar el constructor falso si es necesario, para ver si Object.create o __proto__ son compatibles primero, de esta manera ( ejemplo en vivo ):

function applyConstruct(ctor, params) { var obj, newobj; // Create the object with the desired prototype if (typeof Object.create === "function") { // ECMAScript 5 obj = Object.create(ctor.prototype); } else if ({}.__proto__) { // Non-standard __proto__, supported by some browsers obj = {}; obj.__proto__ = ctor.prototype; if (obj.__proto__ !== ctor.prototype) { // Setting it didn''t work obj = makeObjectWithFakeCtor(); } } else { // Fallback obj = makeObjectWithFakeCtor(); } // Set the object''s constructor obj.constructor = ctor; // Apply the constructor function newobj = ctor.apply(obj, params); // If a constructor function returns an object, that // becomes the return value of `new`, so we handle // that here. if (typeof newobj === "object") { obj = newobj; } // Done! return obj; // Subroutine for building objects with specific prototypes function makeObjectWithFakeCtor() { function fakeCtor() { } fakeCtor.prototype = ctor.prototype; return new fakeCtor(); } }

En Chrome 6, lo anterior usa Object.create ; en Firefox 3.6 y Opera, usa __proto__ . En IE8, utiliza la función de constructor falso.

Lo anterior es bastante simple, pero principalmente trata los problemas que conozco en esta área.


Pienso que otra forma de lograr esto podría ser extender la clase Ext Template para que el constructor del nuevo objeto tome su matriz y haga las cosas que quiere hacer. De esta manera, todo el material de construcción se haría por usted y podría simplemente llamar al constructor de su superclase con sus argumentos.