JavaScript: clona una funzione

Qual è il modo più veloce per clonare una funzione in JavaScript (con o senza le sue proprietà)?

Due opzioni che mi vengono in mente sono eval(func.toString()) e function() { return func.apply(..) } . Ma sono preoccupato per le prestazioni di eval e wrapping che peggioreranno lo stack e probabilmente peggioreranno le prestazioni se applicate molto o applicate a già confezionate.

new Function(args, body) sembra carina, ma in che modo posso esattamente dividere la funzione esistente in arg e body senza un parser JS in JS?

Grazie in anticipo.

Aggiornamento: ciò che intendo è essere in grado di fare

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

prova questo:

 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)); 

Ecco una risposta aggiornata

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

Tuttavia “.bind” è una moderna (> = iE9) funzionalità di JavaScript (con una soluzione alternativa di compatibilità da MDN)

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

Nota: non clonare le proprietà aggiuntive aggiuntive dell’object funzione, inclusa la proprietà prototype . Credito a @jchook

Nota: che la nuova funzione questa variabile è bloccata con l’argomento indicato su bind (), anche sulle nuove chiamate apply (). Credito a @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 

Nota: object funzione binded, instanceof considera newFunc / oldFunc come uguale. Ringraziamo @Christopher

 (new newFunc()) instanceof oldFunc; //gives true (new oldFunc()) instanceof newFunc; //gives true as well newFunc == oldFunc; //gives false however 

Ecco una versione leggermente migliore della risposta di Jared. Questo non finirà con funzioni profondamente annidate più cloni. Chiama sempre l’originale.

 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; }; 

Inoltre, in risposta alla risposta aggiornata fornita da pico.creator, vale la pena notare che la funzione bind() aggiunta in Javascript 1.8.5 ha lo stesso problema della risposta di Jared – manterrà il nesting causando sempre più lentamente funzioni più lente si usa.

Essendo curioso ma ancora incapace di trovare la risposta al tema della performance della domanda di cui sopra, ho scritto questo elenco per nodejs per testare sia le prestazioni che l’affidabilità di tutte le soluzioni presentate (e valutate).

Ho confrontato i tempi di parete di una funzione di clonazione e l’esecuzione di un clone. I risultati insieme agli errori di asserzione sono inclusi nel commento del gist.

Inoltre i miei due centesimi (in base al suggerimento dell’autore):

clone0 cent (più veloce ma più brutto):

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

clone4 cent (più lento ma per coloro che non amano l’eval () per scopi noti solo a loro e ai loro antenati):

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

Per quanto riguarda le prestazioni, se eval / new Function è più lento della soluzione wrapper (e dipende davvero dalla dimensione del corpo della funzione), ti dà clone di funzione nuda (e intendo il vero clone superficiale con proprietà ma stato non condiviso) senza inutili fuzz con proprietà nascoste, funzioni wrapper e problemi con lo stack.

Inoltre, c’è sempre un fattore importante da tenere in considerazione: meno codice, meno spazio per errori.

Lo svantaggio di usare la funzione eval / new è che il clone e la funzione originale opereranno in ambiti diversi. Non funzionerà bene con le funzioni che utilizzano variabili con ambito. Le soluzioni che utilizzano il bind-like wrapping sono indipendenti dall’ambito.

È stato piuttosto eccitante far funzionare questo metodo, quindi rende un clone di una funzione utilizzando la funzione Chiamata.

Alcune limitazioni relative alle chiusure descritte in Riferimento funzione 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; } 

Godere.

Breve e semplice:

 Function.prototype.clone = function() { return new Function('return ' + this.toString())(); }; 
 const oldFunction = params => { // do something }; const clonedFunction = (...args) => oldFunction(...args); 

Mi chiedo solo: perché dovresti voler clonare una funzione quando hai dei prototipi E puoi impostare l’ambito di una chiamata di funzione su qualsiasi cosa desideri?

  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); 

Se vuoi creare un clone usando il costruttore Function, qualcosa di simile dovrebbe funzionare:

 _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 semplice test:

 (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]); } } })() 

Tuttavia, questi cloni perderanno il loro nome e la loro portata per eventuali variabili chiuse.

Ho imposto la risposta di Jared a modo mio:

  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) ora supporta la clonazione dei costruttori (può chiamare con nuovi); in tal caso richiede solo 10 argomenti (è ansible modificarlo) – a causa dell’impossibilità di passare tutti gli argomenti nel costruttore originale

2) tutto è nelle giuste chiusure

 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(); 

Anche se non lo consiglierei mai, ho pensato che sarebbe stata una piccola sfida interessante trovare un clone più preciso prendendo alcune delle pratiche che sembravano essere le migliori e sistemarle un po ‘. Ecco il risultato dei registri:

 Equal? false Names: myFunction myFunction { [Function: myFunction] value: 'something' } { [Function: myFunction] value: 'something', another: 'somethingelse' } InstanceOf? false Called Function Called Function