6

让我们考虑一下我有以下代码

/*...*/
var _fun = fun;
fun = function() {
  /*...*/
  _fun.apply(this, arguments);
}

我刚刚丢失了.length数据,_fun因为我试图用一些拦截逻辑来包装它。

以下不起作用

var f = function(a,b) { };
console.log(f.length); // 2
f.length = 4;
console.log(f.length); // 2

带注释的ES5.1 规范声明如下.length定义

Object.defineProperty(fun, "length", {
  value: /*...*/,
  writable: false,
  configurable: false,
  enumerable: false
}

鉴于里面的逻辑fun要求.length准确,如何在不破坏.length数据的情况下拦截和覆盖这个函数?

我有一种感觉,我需要使用eval狡猾Function.prototype.toString的方法来构造一个具有相同数量参数的新字符串。我想避免这种情况。

4

4 回答 4

3

我知道你更喜欢其他方式,但我能想到的就是用Function构造函数来破解一些东西。凌乱,至少可以说,但它似乎工作:

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);
});
于 2011-09-06T21:22:26.003 回答
2

正确且一致地伪造长度是 javascript 的最后边界,这几乎是它的开始和结束。在一种几乎可以伪造所有内容的语言中,长度仍然有些神奇。ES6 将提供,我们现在可以根据您所使用的引擎和版本,或多或少地伪造它。对于一般的网络兼容性,它还有很长的路要走。Proxies/noSuchMethod 已经在 Mozilla 中使用了一段时间。Proxies 和 WeakMaps 已经在 Chromium 中的 V8 和节点(需要启用标志)中可用,它们提供了正确伪造长度所需的工具。

关于“长度”的详细信息:http: //perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/

最终解决方案:http ://wiki.ecmascript.org/doku.php?id=harmony:proxies + http://wiki.ecmascript.org/doku.php?id=harmony:weak_maps

于 2011-09-07T06:06:49.227 回答
2

为此,我使用以下功能;对于具有合理参数计数的函数来说,它真的很快,比公认的答案更灵活,并且适用于具有超过 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);
}
于 2011-11-19T08:49:40.573 回答
0

如果您需要支持具有任意数量参数的函数,您只需要走eval/路线。Function如果您可以设置合理的上限(我的示例是 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;
  }
}

这种包装代码的方法也可以通过创建不同的where开关来扩展。我已经实现了beforeafter因为它们对我自己的项目最有用——而且around只是因为它让我想起了 lisp。使用此设置,您还可以将包装 ( var f) 移交给外部代码,从而允许您为where关键字开发类似系统的插件,这意味着您可以轻松扩展或覆盖所wrapFunction支持的内容。

显然,您可以随意更改代码的实际包装方式,关键在于使用与 999 和 AdrianLang 类似的技术,而无需担心构建字符串和传递给new Function.

于 2013-02-15T12:30:16.110 回答