Advertencia: esta es una publicación larga.
Hagámoslo simple. Quiero evitar tener que prefijar el nuevo operador cada vez que llamo a un constructor en JavaScript. Esto se debe a que tiendo a olvidarlo, y mi código se arruina mal.
La forma simple de evitar esto es esto ...
function Make(x) {
if ( !(this instanceof arguments.callee) )
return new arguments.callee(x);
// do your stuff...
}
Pero, necesito esto para aceptar la variable no. de argumentos como este ...
m1 = Make();
m2 = Make(1,2,3);
m3 = Make('apple', 'banana');
La primera solución inmediata parece ser el método 'aplicar' como este ...
function Make() {
if ( !(this instanceof arguments.callee) )
return new arguments.callee.apply(null, arguments);
// do your stuff
}
Sin embargo, esto es INCORRECTO: el nuevo objeto se pasa al applymétodo y NO a nuestro constructor arguments.callee.
Ahora, he encontrado tres soluciones. Mi simple pregunta es: cuál parece mejor. O, si tiene un método mejor, dígalo.
Primero , use eval()para crear dinámicamente código JavaScript que llame al constructor.
function Make(/* ... */) {
if ( !(this instanceof arguments.callee) ) {
// collect all the arguments
var arr = [];
for ( var i = 0; arguments[i]; i++ )
arr.push( 'arguments[' + i + ']' );
// create code
var code = 'new arguments.callee(' + arr.join(',') + ');';
// call it
return eval( code );
}
// do your stuff with variable arguments...
}
Segundo : cada objeto tiene una __proto__propiedad que es un enlace 'secreto' a su objeto prototipo. Afortunadamente, esta propiedad se puede escribir.
function Make(/* ... */) {
var obj = {};
// do your stuff on 'obj' just like you'd do on 'this'
// use the variable arguments here
// now do the __proto__ magic
// by 'mutating' obj to make it a different object
obj.__proto__ = arguments.callee.prototype;
// must return obj
return obj;
}
Tercero : esto es algo similar a la segunda solución.
function Make(/* ... */) {
// we'll set '_construct' outside
var obj = new arguments.callee._construct();
// now do your stuff on 'obj' just like you'd do on 'this'
// use the variable arguments here
// you have to return obj
return obj;
}
// now first set the _construct property to an empty function
Make._construct = function() {};
// and then mutate the prototype of _construct
Make._construct.prototype = Make.prototype;
evalLa solución parece torpe y viene con todos los problemas de "evaluación malvada".__proto__la solución no es estándar y el "Gran navegador de MISERY" no la cumple.La tercera solución parece demasiado complicada.
Pero con las tres soluciones anteriores, podemos hacer algo como esto, de lo contrario no podemos ...
m1 = Make();
m2 = Make(1,2,3);
m3 = Make('apple', 'banana');
m1 instanceof Make; // true
m2 instanceof Make; // true
m3 instanceof Make; // true
Make.prototype.fire = function() {
// ...
};
m1.fire();
m2.fire();
m3.fire();
Entonces, efectivamente, las soluciones anteriores nos dan constructores "verdaderos" que aceptan la variable no. de argumentos y no requieren new. Cuál es su opinión sobre esto.
- ACTUALIZACIÓN -
Algunos han dicho "solo arroja un error". Mi respuesta es: estamos haciendo una aplicación pesada con más de 10 constructores y creo que sería mucho más manejable si cada constructor pudiera "inteligentemente" manejar ese error sin lanzar mensajes de error en la consola.
Make()sin newComo make es capitalizado y por lo tanto se asume que es un constructor
new? Porque si es lo último, probablemente estés preguntando en el sitio equivocado. Si es lo primero, es posible que no desee descartar sugerencias sobre el uso de nuevos y la detección de errores tan rápido ... Si su aplicación es realmente "pesada", lo último que desea es algún mecanismo de construcción sobrecargado para ralentizarlo. new, a pesar de todo el flack que recibe, es bastante rápido.