簡體   English   中英

包裝功能和功能。長度

[英]Wrapping functions and function.length

我們考慮一下我有以下代碼

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

我剛剛丟失了_fun上的.length數據,因為我試圖用一些攔截邏輯包裝它。

以下不起作用

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來構造一個具有相同數量參數的新字符串。 我想避免這種情況。

我知道你更喜歡其他方式,但我能想到的只是用Function構造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);
});

正確且一致的偽造長度是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 = ethappxies + http://wiki.ecmascript.org/doku.php?id=harmony:weak_maps

我為此目的使用以下功能; 它對於具有合理參數計數的函數來說非常快,比接受的答案更靈活,適用於具有超過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);
}

如果需要支持具有任意數量參數的函數,則只需沿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只是因為它讓我想起了口齒不清的。 使用這種設置,你也可以手離開包裝( var f )外部代碼,允許你開發類似系統的插件where的關鍵字,這意味着你可以很容易地擴展或覆蓋什么wrapFunction支持。

顯然你可以改變你的代碼實際包裝的方式,關鍵是真的只是使用類似的技術999和AdrianLang,只是不用擔心構建字符串和傳遞給new Function

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM