Quel est le moyen le plus rapide de cloner une fonction en JavaScript (avec ou sans ses propriétés)?
eval(func.toString())
et function() { return func.apply(..) }
. Mais je suis inquiet à propos de la performance de eval et l'empaquetage aggravera la pile et risque de dégrader les performances s'il est appliqué beaucoup ou appliqué à déjà emballé.
new Function(args, body)
a l'air sympa, mais comment puis-je diviser de manière fiable la fonction existante en arguments et corps sans un analyseur JS dans JS?
Merci d'avance.
Mise à jour: Ce que je veux dire, c'est pouvoir faire
var funcB = funcA.clone(); // where clone() is my extension
funcB.newField = {...}; // without affecting funcA
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));
Voici une réponse mise à jour
var newFunc = oldFunc.bind({}); //clones the function with '{}' acting as it's new 'this' parameter
Cependant, ".bind" est une fonctionnalité moderne (> = iE9) de JavaScript (avec une solution de contournement de la compatibilité à partir de MDN).
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
Remarque: qu'il ne clone pas l'objet de fonction additionnel attaché properties, incluant la propriété prototype. Crédit à @jchook
Remarque: que la nouvelle fonction this est collée à l'argument donné sur bind (), même lors de nouveaux appels function apply (). Crédit à @Kevin
function oldFunc() { console.log(this.msg); }
var newFunc = oldFunc.bind( { msg:"You shall not pass!" } ); // this object is binded
newFunc.apply( { msg:"hello world" } ); //logs "You shall not pass!" instead
Remarque: objet de fonction liée, instanceof traite newFunc/oldFunc de la même manière. Crédit à @Christopher
(new newFunc()) instanceof oldFunc; //gives true
(new oldFunc()) instanceof newFunc; //gives true as well
newFunc == oldFunc; //gives false however
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 en Javascript 1.8.5 pose le même problème que la réponse de Jared: elle continuera à imbriquer, ce qui ralentira la création de fonctions à chaque utilisation .
Étant curieux mais toujours incapable de trouver la réponse au sujet de performance de la question ci-dessus, j’ai écrit ceci Gist pour nodejs afin de tester à la fois les performances et la fiabilité de toutes les solutions présentées (et analysées).
J'ai comparé les temps de mur d'une création de fonction de clone et l'exécution d'un clone . Les résultats ainsi que les erreurs d'assertion sont inclus dans le commentaire de Gist.
Plus mes deux cents (sur la suggestion de l'auteur):
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 eval () à des fins connues uniquement par 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;
};
En ce qui concerne les performances, si eval/new Function est plus lent que la solution wrapper (et cela dépend vraiment de la taille du corps de la fonction), il vous donne un clone de fonction nue (et je parle du vrai clone peu profond avec des propriétés mais pas de statut partagé) sans fuzz inutile. avec des propriétés cachées, des fonctions d’emballage et des problèmes de pile.
De plus, vous devez toujours prendre en compte un facteur important: moins il y a de code, moins il y a d'erreurs.
L'inconvénient de l'utilisation de la fonction eval/new est que le clone et la fonction d'origine fonctionneront dans différentes portées. Cela ne fonctionnera pas bien avec les fonctions qui utilisent des variables étendues. Les solutions utilisant un wrapping de type bind sont indépendantes de la portée.
C'était très excitant de faire fonctionner cette méthode, elle crée donc un clone d'une fonction en utilisant l'appel de fonction.
Quelques limitations concernant les fermetures décrites dans Référence de fonction MDN
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.
Court et simple:
Function.prototype.clone = function() {
return new Function('return ' + this.toString())();
};
const oldFunction = params => {
// do something
};
const clonedFunction = (...args) => oldFunction(...args);
Si vous voulez créer un clone en utilisant le constructeur Function, quelque chose comme ceci devrait fonctionner:
_cloneFunction = function(_function){
var _arguments, _body, _result;
var _regexFunction = /^function[\s]+[\w]*\(([\w\s,_\$]*)?\)\{(.*)\}$/;
var _regexArguments = /((?!=^|,)([\w\$_]))+/g;
var _matches = _function.toString().match(_regexFunction)
if(_matches){
if(_matches[1]){
_result = _matches[1].match(_regexArguments);
}else{
_result = [];
}
_result.Push(_matches[2]);
}else{
_result = [];
}
var _clone = Function.apply(Function, _result);
// if you want to add attached properties
for(var _key in _function){
_clone[_key] = _function[_key];
}
return _clone;
}
Un test simple:
(function(){
var _clone, _functions, _key, _subKey;
_functions = [
function(){ return 'anonymous function'; }
,function Foo(){ return 'named function'; }
,function Bar(){ var a = function(){ return 'function with internal function declaration'; }; return a; }
,function Biz(a,boo,c){ return 'function with parameters'; }
];
_functions[0].a = 'a';
_functions[0].b = 'b';
_functions[1].b = 'b';
for(_key in _functions){
_clone = window._cloneFunction(_functions[_key]);
console.log(_clone.toString(), _clone);
console.log('keys:');
for(_subKey in _clone){
console.log('\t', _subKey, ': ', _clone[_subKey]);
}
}
})()
Ces clones perdront leurs noms et leur portée pour toutes les variables fermées sur.
Je me demandais simplement - pourquoi voudriez-vous cloner une fonction lorsque vous avez des prototypes ET pouvez définir l'étendue d'un appel de fonction comme bon vous semble
var funcA = {};
funcA.data = 'something';
funcA.changeData = function(d){ this.data = d; }
var funcB = {};
funcB.data = 'else';
funcA.changeData.call(funcB.data);
alert(funcA.data + ' ' + funcB.data);
J'ai importé la réponse de Jared à ma manière:
Function.prototype.clone = function() {
var that = this;
function newThat() {
return (new that(
arguments[0],
arguments[1],
arguments[2],
arguments[3],
arguments[4],
arguments[5],
arguments[6],
arguments[7],
arguments[8],
arguments[9]
));
}
function __clone__() {
if (this instanceof __clone__) {
return newThat.apply(null, arguments);
}
return that.apply(this, arguments);
}
for(var key in this ) {
if (this.hasOwnProperty(key)) {
__clone__[key] = this[key];
}
}
return __clone__;
};
1) maintenant, il prend en charge le clonage des constructeurs (peut appeler avec new); dans ce cas, prend seulement 10 arguments (vous pouvez le modifier) - impossibilité de passer tous les arguments dans le constructeur
2) tout est dans les fermetures correctes
function cloneFunction(Func, ...args) {
function newThat(...args2) {
return new Func(...args2);
}
function clone() {
if (this instanceof clone) {
return newThat(...args);
}
return Func.apply(this, args);
}
for (const key in Func) {
if (Func.hasOwnProperty(key)) {
clone[key] = Func[key];
}
}
Object.defineProperty(clone, 'name', { value: Func.name, configurable: true })
return clone
};
function myFunction() {
console.log('Called Function')
}
myFunction.value = 'something';
const newFunction = cloneFunction(myFunction);
newFunction.another = 'somethingelse';
console.log('Equal? ', newFunction === myFunction);
console.log('Names: ', myFunction.name, newFunction.name);
console.log(myFunction);
console.log(newFunction);
console.log('InstanceOf? ', newFunction instanceof myFunction);
myFunction();
newFunction();
Bien que je ne recommande jamais d’utiliser ceci, j’ai pensé que ce serait un petit défi intéressant de créer un clone plus précis en prenant certaines des pratiques qui semblaient être les meilleures et en les corrigeant un peu. Voici le résultat des journaux:
Equal? false
Names: myFunction myFunction
{ [Function: myFunction] value: 'something' }
{ [Function: myFunction] value: 'something', another: 'somethingelse' }
InstanceOf? false
Called Function
Called Function
Cette réponse s’adresse aux personnes qui voient le clonage d’une fonction comme la réponse à leur utilisation souhaitée, mais pour lesquelles beaucoup de en fait n'ont pas besoin de cloner une fonction, car ce qu'elles veulent réellement, c'est simplement pouvoir associer différentes propriétés à même fonction, mais ne déclare cette fonction qu’une fois.
Faites cela en créant une fonction créatrice de fonction:
function createFunction(param1, param2) {
function doSomething() {
console.log('in the function!');
}
// Assign properties to `doSomething` if desired, perhaps based
// on the arguments passed into `param1` and `param2`. Or,
// even return a different function from among a group of them.
return doSomething;
};
let a = createFunction();
a.something = 1;
let b = createFunction();
b.something = 2; // does not overwrite a.something
console.log(a.something);
a();
b();
Ce n'est pas exactement le même que celui que vous avez décrit, cependant, cela dépend de la façon dont vous voulez utiliser la fonction que vous souhaitez cloner. Cela utilise également plus de mémoire car il crée en réalité plusieurs copies de la fonction, une fois par appel. Cependant, cette technique peut résoudre le cas d'utilisation de certaines personnes sans la nécessité d'une fonction clone
compliquée.