[英]Wrapping functions and function.length
Let's consider I have the following code 我们考虑一下我有以下代码
/*...*/
var _fun = fun;
fun = function() {
/*...*/
_fun.apply(this, arguments);
}
I have just lost the .length
data on _fun
because I tried to wrap it with some interception logic. 我刚刚丢失了
_fun
上的.length
数据,因为我试图用一些拦截逻辑包装它。
The following doesn't work 以下不起作用
var f = function(a,b) { };
console.log(f.length); // 2
f.length = 4;
console.log(f.length); // 2
The annotated ES5.1 specification states that .length
is defined as follows 带注释的ES5.1规范声明
.length
的定义如下
Object.defineProperty(fun, "length", {
value: /*...*/,
writable: false,
configurable: false,
enumerable: false
}
Given that the logic inside fun
requires .length
to be accurate, how can I intercept and overwrite this function without destroying the .length
data? 鉴于内部的逻辑
fun
要求.length
是准确的,我怎么能截获并覆盖该功能在不破坏.length
数据?
I have a feeling I will need to use eval
and the dodgy Function.prototype.toString
to construct a new string with the same number of arguments. 我有一种感觉,我将需要使用
eval
和狡猾的Function.prototype.toString
来构造一个具有相同数量参数的新字符串。 I want to avoid this. 我想避免这种情况。
I know you'd prefer some other way, but all I can think of is to hack together something with the Function
constructor. 我知道你更喜欢其他方式,但我能想到的只是用
Function
构造Function
来破解一些东西。 Messy, to say the least, but it seems to work: 凌乱,至少可以说,但似乎有效:
var replaceFn = (function(){
var args = 'abcdefghijklmnopqrstuvwxyz'.split('');
return function replaceFn(oldFn, newFn) {
var argSig = args.slice(0, oldFn.length).join(',');
return Function(
'argSig, newFn',
'return function('
+ argSig +
'){return newFn.apply(this, arguments)}'
)(argSig, newFn);
};
}());
// Usage:
var _fun = fun;
fun = replaceFn(fun, function() {
/* ... */
_fun.apply(this, arguments);
});
Faking length correctly and consistently is the final frontier in javascript and that's pretty much the beginning and end of it. 正确且一致的伪造长度是javascript中的最后边界,这几乎是它的开始和结束。 In a language where you can fake just about everything, length is still somewhat magical.
在一种你可以假装几乎所有东西的语言中,长度仍然有些神奇。 ES6 will deliver, and we can fake it now to greater and lesser degrees depending which engine and version you're in. For general web compatability it's a ways off.
ES6将提供,我们现在可以根据您所使用的引擎和版本来更大和更小程度地伪造它。对于一般的网络兼容性,它是一个方法。 Proxies/noSuchMethod has been in Mozilla for a while.
Proxies / noSuchMethod已经在Mozilla工作了一段时间。 Proxies and WeakMaps have gotten to usable in V8 in Chromium and and node (requiring flags to enable) which provide the tool you need to fake length correctly.
Proxies和WeakMaps已经可以在Chromium中的V8和节点(需要启用标志)中使用,它们提供了正确伪造长度所需的工具。
In detail on "length": http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/ 关于“长度”的详细信息: http : //perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/
The eventual solution: http://wiki.ecmascript.org/doku.php?id=harmony:proxies + http://wiki.ecmascript.org/doku.php?id=harmony:weak_maps 最终的解决方案: http : //wiki.ecmascript.org/doku.php?id = eth : appxies + http://wiki.ecmascript.org/doku.php?id=harmony:weak_maps
I use the following function for this purpose; 我为此目的使用以下功能; it's really fast for functions with reasonable parameter counts, more flexible than the accepted answer and works for functions with more than 26 parameters.
它对于具有合理参数计数的函数来说非常快,比接受的答案更灵活,适用于具有超过26个参数的函数。
function fakeFunctionLength(fn, length) {
var fns = [
function () { return fn.apply(this, arguments); },
function (a) { return fn.apply(this, arguments); },
function (a, b) { return fn.apply(this, arguments); },
function (a, b, c) { return fn.apply(this, arguments); },
function (a, b, c, d) { return fn.apply(this, arguments); },
function (a, b, c, d, e) { return fn.apply(this, arguments); },
function (a, b, c, d, e, f) { return fn.apply(this, arguments); }
], argstring;
if (length < fns.length) {
return fns[length];
}
argstring = '';
while (--length) {
argstring += ',_' + length;
}
return new Function('fn',
'return function (_' + argstring + ') {' +
'return fn.apply(this, arguments);' +
'};')(fn);
}
You only need to go down the eval
/ Function
route if you need to support functions with any number of parameters. 如果需要支持具有任意数量参数的函数,则只需沿
eval
/ Function
路径下行。 If you can set a reasonable upper limit (my example is 5) then you can do the following: 如果您可以设置合理的上限(我的示例是5),那么您可以执行以下操作:
var wrapFunction = function( func, code, where ){
var f;
switch ( where ) {
case 'after':
f = function(t,a,r){ r = func.apply(t,a); code.apply(t,a); return r; }
break;
case 'around':
f = function(t,a){ return code.call(t,func,a); }
break;
default:
case 'before':
f = function(t,a){ code.apply(t,a); return func.apply(t,a); }
break;
}
switch ( func.length ) {
case 0: return function(){return f(this, arguments);}; break;
case 1: return function(a){return f(this, arguments);}; break;
case 2: return function(a,b){return f(this, arguments);}; break;
case 3: return function(a,b,c){return f(this, arguments);}; break;
case 4: return function(a,b,c,d){return f(this, arguments);}; break;
case 5: return function(a,b,c,d,e){return f(this, arguments);}; break;
default:
console.warn('Too many arguments to wrap successfully.');
break;
}
}
This method of wrapping code is also extendable by creating different where
switches. 这种包装代码的方法也可以通过创建不同的
where
开关来扩展。 I've implemented before
and after
because they are the most useful for my own project — and around
just because it reminds me of lisp. 我实现了
before
和after
,因为他们是为我自己的项目最有用的-和around
只是因为它让我想起了口齿不清的。 Using this set-up you could also hand off the wrapping ( var f
) to external code allowing you to develop a plugin like system for where
keywords, meaning that you could easily extend or override what wrapFunction
supported. 使用这种设置,你也可以手离开包装(
var f
)外部代码,允许你开发类似系统的插件where
的关键字,这意味着你可以很容易地扩展或覆盖什么wrapFunction
支持。
Obviously you can change how the code is actually wrapped however you like, the key really is just using a similar technique to 999 and AdrianLang, just without worrying about building strings and passing to new Function
. 显然你可以改变你的代码实际包装的方式,关键是真的只是使用类似的技术999和AdrianLang,只是不用担心构建字符串和传递给
new Function
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.