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 apply
mé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;
eval
La 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.
fuente
Make()
sinnew
Como make es capitalizado y por lo tanto se asume que es un constructornew
? 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.