简体   繁体   English

包装功能和功能。长度

[英]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 = ethappxies + 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. 我实现了beforeafter ,因为他们是为我自己的项目最有用的-和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.

相关问题 Function.length与this.length不同。 为什么? - Function.length is not the same as this.length. Why? 如何在严格模式下重写Function.length以返回数组的长度 - How to override Function.length to return the length of an array, in strict mode JS中function.length和arguments.length有什么区别 - What is the difference between function.length and arguments.length in JS 为什么`Array.length`,`Function.length`,`String.length`等返回1? - Why does `Array.length`, `Function.length`, `String.length`, etc return 1? 由于&#39;函数&#39;的MDN描述混淆,Function.length是Function或Function.prototype的属性 - confused by MDN description on 'Function', Function.length is property of Function or Function.prototype 用括号括起的函数-(function(){})()和`.call()` - Brackets wrapping of functions - (function() { }) () , and `.call()` Javascript将链接的函数包装在单个函数中 - Javascript wrapping chained functions in a single function bluebird是否具有用于在promises中包装函数的说服功能? - Does bluebird have a convince function for wrapping functions in promises? 自动将动态添加的对象属性(函数)包装在包装函数中 - Automatically wrapping dynamically added object properties (functions) in a wrapper function 将函数包装到匿名函数中后,为什么函数不起作用? - Why don't functions work after wrapping them in an anonymous function?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM