Une mauvaise saisie en javascript est d'oublier d'appeler new
sur une fonction destinée à être instanciée, conduisant à this
être lié à un objet différent (généralement le global) au lieu d'un nouveau. Une solution que j'ai lu est sur le point de vérifier explicitement dans la fonction constructeur en utilisant l'idiome suivant:Suppression de la nécessité de "nouveau"
function SomeConstructor(x, y, ...) {
// check if `this` is created with new
if (!(this instanceof arguments.callee))
return new SomeConstructor(x, y, ...);
// normal initialization code follows
maintenant new SomeConstructor(...)
et SomeConstructor(...)
sont équivalentes.
Je voudrais simplifier cela en créant une fonction wrapper factory(fn)
qui fait les deux premières lignes, puis délègue à la fonction enveloppée fn
. Ce serait utilisé comme:
SomeConstructor = factory(function (x, y, ...) {
// normal initialization code follows
})
Ma première tentative a été:
function factory(fn) {
return function() {
if (!(this instanceof arguments.callee)) {
return new arguments.callee.apply(this, arguments);
}
fn.apply(this, arguments);
}
}
mais il échoue avec "appelé Function.prototype.apply sur incompatibles [object Object]". La deuxième tentative a été:
function factory(fn) {
return function() {
if (!(this instanceof arguments.callee)) {
var tmp = new arguments.callee();
arguments.callee.apply(tmp, arguments);
return tmp;
}
fn.apply(this, arguments);
}
}
Ce genre de travaux, mais il peut appeler deux fois la fonction enveloppée: une fois sans argument (pour créer une nouvelle instance) et une fois avec les arguments transmis pour l'initialisation réelle. Apparemment, c'est fragile et inefficace, mais je ne peux pas trouver un moyen de le faire avec un seul appel. Est-ce possible ?
EDIT: Sur la base de l'approche de bobince, voici un semblable qui fait l'affaire:
function factory(init) {
var run_init = true;
function constr() {
if (!(this instanceof constr)) {
run_init = false;
var tmp = new constr();
run_init = true;
init.apply(tmp, arguments);
return tmp;
}
if (run_init)
init.apply(this, arguments);
}
return constr;
}
Quant à savoir si cela est quelque chose qui devrait être encouragé ou non, est discutable. Je viens d'un fond de Python et je pense à new
comme bruit (Java) ou wart (Javascript), mais il me manque peut-être quelque chose.
Je vous recommande de ne pas essayer d'éliminer l'utilisation d'une construction de langage simplement parce que vous pourriez accidentellement oublier de l'inclure. On dirait qu'il y a énormément de travail pour ne pas avoir à taper trois caractères de toute façon. –
@Darrell: Il ne s'agit pas tant d'éviter le travail que d'essayer d'utiliser la métaprogrammation pour faciliter la vie de tout le monde. C'est la norme dans les langages dynamiques comme Javascript, Ruby, et al. –
@John Je comprends et je suis tout pour faciliter la vie, mais je ne vois toujours pas comment tout ce code est plus facile que de taper «nouveau». :) –