Aviso: Este é um post longo.
Vamos simplificar. Quero evitar ter que prefixar o novo operador toda vez que chamo um construtor em JavaScript. Isso ocorre porque costumo esquecê-lo e meu código estraga tudo.
A maneira simples de contornar isso é isso ...
function Make(x) {
if ( !(this instanceof arguments.callee) )
return new arguments.callee(x);
// do your stuff...
}
Mas, eu preciso disso para aceitar a variável no. de argumentos, assim ...
m1 = Make();
m2 = Make(1,2,3);
m3 = Make('apple', 'banana');
A primeira solução imediata parece ser o método 'apply' como este ...
function Make() {
if ( !(this instanceof arguments.callee) )
return new arguments.callee.apply(null, arguments);
// do your stuff
}
No entanto, isso está errado - o novo objeto é passado para o apply
método e NÃO para o nosso construtor arguments.callee
.
Agora, eu vim com três soluções. Minha pergunta simples é: qual parece melhor. Ou, se você tiver um método melhor, informe.
Primeiro - use eval()
para criar dinamicamente o código JavaScript que chama o construtor.
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 - Todo objeto tem __proto__
propriedade que é um link 'secreto' para o seu objeto protótipo. Felizmente, essa propriedade é gravável.
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;
}
Terceiro - isso é algo semelhante à segunda solução.
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
solução parece desajeitada e vem com todos os problemas de "avaliação ruim".__proto__
solução não é padrão e o "Grande Navegador da Misericórdia" não a honra.A terceira solução parece excessivamente complicada.
Mas com todas as três soluções acima, podemos fazer algo assim, que não podemos de outra forma ...
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();
Tão efetivamente as soluções acima nos dão construtores "verdadeiros" que aceitam a variável no. de argumentos e não exige new
. Qual é a sua opinião sobre isso.
- ATUALIZAÇÃO -
Alguns disseram "apenas jogue um erro". Minha resposta é: estamos fazendo um aplicativo pesado com mais de 10 construtores e acho que seria muito mais difícil se todos os construtores pudessem "inteligentemente" lidar com esse erro sem lançar mensagens de erro no console.
Make()
sem new
devido Make é capitalizado e, portanto, assume que é um construtor
new
? Porque se for o último, você provavelmente está perguntando no site errado. Se for o primeiro, convém não ignorar sugestões sobre o uso de novos e detectar erros tão rapidamente ... Se seu aplicativo é realmente "pesado", a última coisa que você quer é um mecanismo de construção exagerado para abrandá-lo. new
, por todo o flack que obtém, é bem rápido.