JavaScript:克隆一个函数
在JavaScript中克隆函数的最快方法是什么(有或没有它的属性)?
想到两个选项是eval(func.toString())
和function() { return func.apply(..) }
。 但是我担心eval的性能,并且包装会使堆栈变得更糟,并且如果应用了很多或应用于已经包装的,可能会降低性能。
new Function(args, body)
看起来不错,但是如何可靠地将已有的函数拆分为args和body,而JS中没有JSparsing器?
提前致谢。
更新:我的意思是能够做到
var funcB = funcA.clone(); // where clone() is my extension funcB.newField = {...}; // without affecting funcA
尝试这个:
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));
这是一个更新的答案
var newFunc = oldFunc.bind({}); //clones the function with '{}' acting as it's new 'this' parameter
然而,“.bind”是JavaScript的一个现代(> = iE9)function(与MDN兼容的解决方法)
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
注意:它不克隆函数对象附加附加属性 , 包括 原型属性。 感谢@jchook
注意:即使在新的函数apply()调用中, 该variables的新函数仍然与bind()上给出的参数一致。 感谢@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
注意:绑定的函数对象instanceof将newFunc / oldFunc视为相同。 感谢@Christopher
(new newFunc()) instanceof oldFunc; //gives true (new oldFunc()) instanceof newFunc; //gives true as well newFunc == oldFunc; //gives false however
这是Jared的答案稍微好一点的版本。 这一个不会最终克隆得越深嵌套的function。 它总是调用原件。
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; };
另外,为了响应pico.creator给出的更新的答案,值得注意的是,在Javascript 1.8.5中添加的bind()
函数与Jared的答案相同 – 它将继续嵌套,从而导致每次更慢和更慢的函数用来。
虽然好奇,但仍然无法find上述问题的性能主题的答案,我写了这个要求nodejs的要点 ,以testing所有提供(和得分)的解决scheme的性能和可靠性。
我比较了克隆函数创build和克隆执行的时间。 结果和断言错误包含在要点中。
加上我的两分钱(根据作者的build议):
克隆0分(更快但更丑):
Function.prototype.clone = function() { var newfun; eval('newfun=' + this.toString()); for (var key in this) newfun[key] = this[key]; return newfun; };
clone4 cent(较慢,但对于那些不喜欢eval()的人来说只是为了他们和他们的祖先才知道的):
Function.prototype.clone = function() { var newfun = new Function('return ' + this.toString())(); for (var key in this) newfun[key] = this[key]; return newfun; };
至于性能方面,如果eval / new函数比包装器解决scheme慢(它实际上取决于函数体的大小),它给你一个裸函数clone(我的意思是具有属性但是没有共享状态的真正的浅层克隆)而没有不必要的模糊与隐藏的属性,包装function和堆栈问题。
另外还有一个重要的因素需要考虑:代码越less,出错的地方就越less。
使用eval / new函数的缺点是克隆和原始函数将在不同的作用域中运行。 对于使用作用域variables的函数来说,它不能很好地工作。 使用类似绑定的包装的解决scheme是独立于作用域的。
使这个方法工作是非常令人兴奋的,所以它使用函数调用来克隆一个函数。
MDN函数参考中描述的有closures包的一些限制
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; }
请享用。
我以我自己的方式提出了Jared的回答:
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)现在它支持克隆构造函数(可以用new调用); 在这种情况下只有10个参数(你可以改变它) – 由于不可能在原来的构造函数中传递所有参数
2)一切都在正确的closures
只是想知道 – 为什么你想克隆一个函数,当你有原型,并可以设置一个函数调用的范围,你想什么?
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);
简单而简单:
Function.prototype.clone = function() { return new Function('return ' + this.toString())(); };
如果你想使用Function构造函数创build一个克隆,那么应该这样工作:
_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; }
一个简单的testing:
(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]); } } })()
尽pipe如此,这些克隆将失去它们的名称和范围。
那么,根据现有的答案,每一件事情都是清楚的。 主要重要的是,为什么我们应该去做一个函数的克隆,而如果我们有一个函数的引用(即使是克隆我们需要一个引用),可以使用任何范围来调用函数,
所以,即使我们需要另一个也会这样做,我们也有这个答案。 只是添加到它
function cloneFunction(fnToClone){ var fnRefName = 'cloneFn'; eval('var ' + fnRefName + ' = ' + fnToClone.toString()); return eval(fnRefName); }
如果你真的想克隆代码作为新的代码副本,你可以做到这一点,如果你想,你可以添加克隆到函数原型就像上面一样。 很明显,一旦你得到一个副本,你可以迭代添加复制属性。 是不推荐eval,也不build议克隆一个函数,只是添加另一个视图/答案,因为在所有其他情况下,最终它会调用原始函数,当你试图调用你的克隆的时候,我的意思是参考你必须使用原来的一个。 (可以评论,而不是新的答案,但不幸的是,我没有评论访问迄今)