2012-07-15 19 views
6

utilizo la siguiente función para crear instancias de funciones en JavaScript de una serie de argumentos:funciones Instantiate JavaScript con prototipos personalizados

var instantiate = function (instantiate) { 
    return function (constructor, args, prototype) { 
     "use strict"; 

     if (prototype) { 
      var proto = constructor.prototype; 
      constructor.prototype = prototype; 
     } 

     var instance = instantiate(constructor, args); 
     if (proto) constructor.prototype = proto; 
     return instance; 
    }; 
}(Function.prototype.apply.bind(function() { 
    var args = Array.prototype.slice.call(arguments); 
    var constructor = Function.prototype.bind.apply(this, [null].concat(args)); 
    return new constructor; 
})); 

Uso de la función de arriba se puede crear instancias de la siguiente manera (ver la fiddle):

var f = instantiate(F, [], G.prototype); 

alert(f instanceof F); // false 
alert(f instanceof G); // true 

f.alert(); // F 

function F() { 
    this.alert = function() { 
     alert("F"); 
    }; 
} 

function G() { 
    this.alert = function() { 
     alert("G"); 
    }; 
} 

El código anterior funciona para los constructores construida por el usuario como F. Sin embargo, no funciona para constructores nativos como Array por razones obvias de seguridad. Siempre puedes crear una matriz y luego cambiar su propiedad __proto__ pero estoy usando este código en Rhino para que no funcione allí. ¿Hay alguna otra forma de lograr el mismo resultado en JavaScript?

Respuesta

6

No puede fully subclass an array. No se puede usar Object.create para eliminar mucha complejidad de su código actual (ex).

+0

¿Cómo utilizaría 'Object.create' para eliminar la complejidad de mi código? Le agradecería si pudiera escribir una demostración. –

+0

He agregado un enlace de ejemplo a mi respuesta. Tenga en cuenta que no funciona en la versión de rinoceronte de Ideone (pero funciona bien con la mía, 1.7r3). –

+0

Su método funciona para la mayoría de los constructores. Sin embargo, causa [problemas] (http://jsfiddle.net/MMp5u/2/) cuando se usa una función de fábrica como constructor (por ejemplo, 'Array'). El uso de 'Object.create (Array.prototype)' crea un objeto que es un 'instanceof' del constructor' Array'. Sin embargo, no es en realidad una matriz. Mi función puede parecer un poco más compleja, pero evita [evita] (http://jsfiddle.net/MMp5u/1/) los problemas que presenta su función. –

3

No creo que esté logrando lo que pretende aquí. Primero en sus funciones F y G usted están definiendo una función de alerta en este objeto. Esto significa que cada vez que instancia un objeto se creará un nuevo objeto de función y se le asignará una alerta. Esto no es lo que quiere, es necesario definir alerta en el prototipo del F y G.

function F() { } 

F.prototype.alert = function() { 
    alert("F"); 
}; 

function G() { } 

G.prototype.alert = function() { 
    alert("G"); 
}; 

Sin embargo, usted todavía tiene un problema en su función instantiate. Si se llama a la forma que tiene

var f = instantiate(F, [], G.prototype); 

todo lo que está haciendo es establecer el prototipo de f para G.prototype, que no es lo que creo que quiere. Supongo que si crea una instancia de un objeto F, querría poder invocar todas las funciones definidas en F.prototype, pero tal como están las cosas, este no es el caso.

function F() { } 

F.prototype.alert = function() { 
    alert("F"); 
}; 

F.prototype.foo = function() { 
    alert("F foo"); 
}; 

function G() { } 

G.prototype.alert = function() { 
    alert("G"); 
}; 


var f = instantiate(F, [], G.prototype); 
f.foo(); // error! 

El motivo del error aquí es como he dicho que acaba de asignar prototipo de f para G.prototype y G.prototype no tiene una función foo definido.

Si usted está buscando para hacer la herencia de esta manera echar un vistazo a el blog de John Resig que tiene una buena implementación de dichas mejoras: http://ejohn.org/blog/simple-javascript-inheritance/

también Douglas Crockford ha preparado una serie de buenos ejemplos: http://www.crockford.com/javascript/inheritance.html

+0

Haha. En realidad, eso es precisamente lo que quiero. Creé una función 'instanciar' que acepta una función' constructor' y una lista 'args' y devuelve una' instancia' del 'constructor'. Si pasa un objeto 'prototype' opcional como tercer parámetro, entonces establece la propiedad interna' [[proto]] 'de' instance' a ese 'prototype'. Este es el comportamiento previsto. Entonces cuando llamo 'instanciar (F, [], G.prototype)' entonces debería crear una 'instancia' de' F' pero debería usar 'G.prototype' en lugar de' F.prototype' como 'prototype' de la 'instancia'. –

+0

La razón por la que escribí la función 'alert' dentro de' F' y 'G' es para mostrar la diferencia de que aunque' f' está construido por 'F' (y por lo tanto' f.alert() 'muestra' F') es La propiedad interna '[[proto]]' se establece en 'G.prototype'. Por lo tanto, es un 'instanceof' la función' G'. –

+0

Si lo piensas sería contra-intuitivo tener un tercer parámetro 'prototype' opcional si nunca hubiera querido cambiar la propiedad' [[proto]] 'interna de' f' de 'F.prototype' a' G .prototype'. Como es opcional, si lo hubiera dejado, 'f' heredará de' F.prototype' (que es el comportamiento que usted propuso es el correcto). Sin embargo, la intención es modificar la propiedad interna '[[proto]]' de la 'instancia' sin la necesidad de modificar manualmente' __proto__'. –

Cuestiones relacionadas