2011-09-06 5 views
6

は、私はいくつかの傍受ロジックでそれをラップしようとしたので、私はちょうど_fun.lengthデータを失っているのは、私は次のコードラッピング機能とfunction.length

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

を持って考えてみましょう。

次は

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

fun内部のロジックがどのように私は傍受して上書きすることができ、正確であると.lengthが必要であることを考えると

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

を次のように.lengthが定義されていることをannotated ES5.1 specification states動作しません。この機能はデータを破壊することなく.lengthですか?

私はevalと同じ数の引数を持つ新しい文字列を構築するためにはドーフFunction.prototype.toStringを使用する必要があると感じています。私はこれを避けたい。

+0

本当に 'length'を正しく設定する必要がありますか? https://developer.mozilla.org/ja/JavaScript/Reference/Global_Objects/Function/bindを見てみると、多くのライブラリもこの動作をシミュレートします。 – Prusse

+0

@Prusseバインドはどのように私の問題を解決するつもりですか? – Raynos

+0

あなたの目的を明確に理解することはできません。申し訳ありません。 – Prusse

答えて

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); 
}); 
+0

汚いですが、まだnice :) –

2

偽造の長さが正しく一貫JavaScriptで最後のフロンティアであり、それはかなりそれの始まりと終わりです。あなたがすべてを偽造することができる言語では、長さはやや魔法です。 ES6は提供してくれるでしょうし、あなたが現在いるエンジンとバージョンに応じて、今度はそれを大きく下げていくことができます。一般的なWeb互換性については、それは道のりです。 Proxies/noSuchMethodはしばらくMozillaにありました。プロキシとウィークマップはV8でChromiumとノードで使用できるようになりました(有効にするフラグが必要です)。これは、長さを正しく模倣するために必要なツールを提供します。 "長さ" に詳細に

http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/

最終的溶液:+ http://wiki.ecmascript.org/doku.php?id=harmony:weak_mapshttp://wiki.ecmascript.org/doku.php?id=harmony:proxies

+0

プロキシがどのように偽の長さを助けることができるか説明できますか?弱い地図がどうすれば助けになるのでしょうか?また、 "長さ"は関数ではない配列を指します。 – Raynos

+0

プロキシを使用すると、インデックス用のキャッチオールゲッターを作成し、ネイティブな長さのプロパティに一致するすべての必要な動作を複製できます。 WeakMapsは、タイプ/未定義チェックが行われる前にプロキシが突き止める存在しないプロパティのアクセサを提供するために利用するものです。あなたの偽の配列上の401042のgetterにアクセスするときは、最初にそのプロパティをWeakMapを使って定義してから、偽の長さプロパティの値を変更します。 すべてのネイティブレングスプロパティは、JavaScriptsレルム外の実際のデータ構造への内部ポインタであるため、 "魔法の"動作を示すことができます。 –

+0

関数の長さを変更することは、配列よりも使い勝手が劣るようですが、それでもなお魔法のように染み込んでいます。この場合、ブラウザ間で予期せず動作するだけです。 Mozillaはサイズを変更し、明らかに私が見たことはありません。 V8では、設定時に指定した数値が返されますが、実際に値は変更されず、何をしても実際のパラメータ数が返されます。 Lengthは、それが表すものを保持するメモリへの直接マッピングであると考えられます。 –

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); 
} 
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)を渡すことができます。つまり、wrapFunctionを簡単に拡張または上書きできます。

明らかにコードを実際にラップする方法を変更することができますが、キーは実際には文字列を作成してnew Functionに渡すことを心配することなく999とAdrianLangに同様のテクニックを使用しています。

関連する問題