156 votes

JavaScript: cloner une fonction

Quel est le moyen le plus rapide pour cloner une fonction en JavaScript (avec ou sans ses propriétés)?

Deux options venant à l'esprit sont eval(func.toString()) et function() { return func.apply(..) }. Mais je suis inquiet au sujet de la performance de la fonction eval et habillage de faire de la pile pire et sera probablement dégrader les performances si elle est appliquée à un lot ou appliqué à déjà enveloppé.

new Function(args, body) a l'air sympa, mais exactement comment puis-je fiable split fonction existante pour args et le corps sans JS analyseur en JS?

Merci à l'avance.

Mise à jour: Ce que je veux dire, c'est être capable de le faire

var funcB = funcA.clone(); // where clone() is my extension
funcB.newField = {...};    // without affecting funcA

147voto

pico.creator Points 4094

Voici une mise à jour de réponse

var newFunc = oldFunc.bind({}); //clones the function with '{}' acting as it's new 'this' parameter

Cependant ".bind" est une nouvelle fonctionnalité de JavaScript (avec une compatibilité solution de contournement de l'Mdn)

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind

Remarque: il n'est pas de cloner l'objet de fonction supplémentaire attaché propriétés, y compris le prototype de la propriété.

64voto

Jared Points 3852

essaye ça:

 var x = function() {
    return 1;
};

var t = function(a,b,c) {
    return a+b+c;
};


Function.prototype.clone = function() {
    var that = this;
    var temp = function temporary() { return that.apply(this, arguments); };
    for(var key in this) {
        if (this.hasOwnProperty(key)) {
            temp[key] = this[key];
        }
    }
    return temp;
};

alert(x === x.clone());
alert(x() === x.clone()());

alert(t === t.clone());
alert(t(1,1,1) === t.clone()(1,1,1));
alert(t.clone()(1,1,1));
 

24voto

Justin Warkentin Points 1620

Voici une version légèrement meilleure de la réponse de Jared. Celui-ci ne se retrouvera pas avec des fonctions profondément imbriquées plus vous clonerez. Il appelle toujours l'original.

 Function.prototype.clone = function() {
    var cloneObj = this;
    if(this.__isClone) {
      cloneObj = this.__clonedFrom;
    }

    var temp = function() { return cloneObj.apply(this, arguments); };
    for(var key in this) {
        temp[key] = this[key];
    }

    temp.__isClone = true;
    temp.__clonedFrom = cloneObj;

    return temp;
};
 

De plus, en réponse à la réponse mise à jour donnée par pico.creator, il convient de noter que la fonction bind() ajoutée dans Javascript 1.8.5 pose le même problème que la réponse de Jared - elle continuera à imbriquer, ce qui ralentira fonctionne chaque fois qu'il est utilisé.

14voto

royaltm Points 61

D'être curieux, mais toujours pas à trouver la réponse à l'exécution sujet de la question ci-dessus, j'ai écrit ce résumé pour nodejs pour tester à la fois la performance et la fiabilité de tous présentés (et marqué) solutions.

J'ai comparé le mur le temps d'un clone de la fonction de la création et de l'exécution d'un clone. Les résultats obtenus avec l'affirmation des erreurs sont inclus dans le résumé du commentaire.

De Plus mes deux cents (basé sur l'auteur de la suggestion):

clone0 cent (plus rapide mais plus laid):

Function.prototype.clone = function() {
  var newfun;
  eval('newfun=' + this.toString());
  for (var key in this)
    newfun[key] = this[key];
  return newfun;
};

clone4 cent (plus lent mais pour ceux qui n'aiment pas la fonction eval() pour des raisons connues seulement pour eux et leurs ancêtres):

Function.prototype.clone = function() {
  var newfun = new Function('return ' + this.toString())();
  for (var key in this)
    newfun[key] = this[key];
  return newfun;
};

Comme pour les performances, si eval/nouvelle Fonction est plus lent que le wrapper solution (et cela dépend vraiment de la fonction de la taille du corps), il vous donne la nue fonction clone (et je veux dire le vrai clone simple mais avec des propriétés de partage de l'état), sans inutiles fuzz avec les propriétés cachées, des fonctions wrapper et des problèmes avec la pile.

De Plus, il existe toujours un facteur important que vous devez prendre en compte: - le moins de code, moins de place pour les erreurs.

L'inconvénient de l'utilisation de la fonction eval/Fonction nouvelle, c'est que le clone et l'original de la fonction opèrent dans des champs d'application différents. Il ne marche pas bien avec les fonctions qui sont à l'aide de portée des variables. Les solutions à l'aide de bind-comme d'emballage sont à la portée indépendante.

10voto

Max Dolgov Points 31

C'était très excitant de faire fonctionner cette méthode, elle crée donc un clone d'une fonction à l'aide d'un appel de fonction.

Quelques limitations concernant les fermetures décrites dans MDN Function Reference

 function cloneFunc( func ) {
  var reFn = /^function\s*([^\s(]*)\s*\(([^)]*)\)[^{]*\{([^]*)\}$/gi
    , s = func.toString().replace(/^\s|\s$/g, '')
    , m = reFn.exec(s);
  if (!m || !m.length) return; 
  var conf = {
      name : m[1] || '',
      args : m[2].replace(/\s+/g,'').split(','),
      body : m[3] || ''
  }
  var clone = Function.prototype.constructor.apply(this, [].concat(conf.args, conf.body));
  return clone;
}
 

Prendre plaisir.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X