22

EcmaScript-5Function.prototype.bindの実装について非常に興味深い質問があります。通常、bindを使用する場合は、次のようにします。

var myFunction = function() {
    alert(this);
}.bind(123);

// will alert 123
myFunction();

かっこいいですが、これを行うとどうなるでしょうか。

// rebind binded function
myFunction = myFunction.bind('foobar');
// will alert... 123!
myFunction();

Function.prototype.bindの実装方法に関しては完全に論理的な動作であることを理解しています(https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind)。しかし、実際の状況では、それは完全に役に立たない行動ですよね?問題は、それがバグなのか機能なのかということです。それがバグなら、なぜそれはどこにも言及されていないのですか?それが機能である場合、ネイティブの「バインド」実装を備えたGoogleChromeがまったく同じように動作するのはなぜですか。

より明確にするために、私の意見ではもっと理にかなっていることですが、Function.prototype.bindを少し異なる方法で実装するコードスニペットを次に示します。

if (!Function.prototype.bind) {
    Function.prototype.bind = function() {
        var funcObj = this;
        var original = funcObj;
        var extraArgs = Array.prototype.slice.call(arguments);
        var thisObj = extraArgs.shift();
        var func = function() {
            var thatObj = thisObj;
            return original.apply(thatObj, extraArgs.concat(
                Array.prototype.slice.call(
                    arguments, extraArgs.length
                )
            ));
        };
        func.bind = function() {
            var args = Array.prototype.slice.call(arguments);
            return Function.prototype.bind.apply(funcObj, args);
        }
        return func;
    };
}

だから今これを試してみてください:

// rebind binded function
myFunction = myFunction.bind('foobar');
// will alert... "foobar"
myFunction();

私の意見では、「これ」を置き換える方が理にかなっています...

それで、皆さんはそれについてどう思いますか?

4

2 に答える 2

14

の前例Function.prototype.bindは、さまざまなJSフレームワークでのアイデアの実装でした。私の知る限り、それらのどれも、this後続のバインディングによって-バインディングを変更することを許可していませんでした。ES5がそれを許可しない理由を尋ねるのと同様に、なぜそれらのどれもこの結合の変更を許可しなかったのかを尋ねたほうがよいでしょう。

私が聞いた中で、これを奇妙だと思ったのはあなただけではありません。(私と同じように)MozillaのJSエンジンで作業しているChris Learyは、それは少し奇妙だと思い、数か月前にTwitterで問題を提起しました。そして、多少異なる形で、Mozilla Labsのハッカーの1人が、関数を「バインド解除」して、そこからターゲット関数を抽出する方法があるかどうかを質問したことを覚えています。(それができれば、もちろん、別thisの引数にバインドすることもできます。少なくとも、バインドされた引数リストを抽出して渡すこともできます。)

指定されたときに議論されていた問題を覚えていませんbind。ただし、このようなものがハッシュ化された時点では、es-discussメーリングリストには特に注意を払っていませんでした。とは言うものの、ES5は、フレーズを借りるために、この地域であまり革新を求めていなかったと思います。

十分に詳細な提案を書いた場合、es-discussにこれらの懸念に対処するためのいくつかの内省的な方法を提案できる可能性があります。一方、バインディングは情報隠蔽メカニズムの一形態であり、その採用を妨げることになります。時間があれば、何か提案してみる価値があるかもしれません。私の推測では、情報隠蔽の懸念が提案の採用を妨げるだろうと思います。しかし、それは間違いである可能性がある単なる推測です。見つける唯一の方法...

于 2011-09-06T01:30:13.320 に答える
2

関数をバインドするときは、それ自体のこの疑似引数を無視し、このための固定値を使用して元の関数を呼び出す新しい関数を要求します。

この関数を別のときにバインドすると、まったく同じ動作になります。bindが何らかの形で新しいthisをパッチする場合、すでにバインドされている関数の場合は特別な場合が必要になります。

言い換えると、bindは、bindによって返される関数で機能するのとまったく同じように「通常の」関数で機能します。オーバーライドする要素がない場合は、関数のセマンティックの複雑さを低く抑えることをお勧めします。一部の入力関数を特別に処理するのではなく、すべての入力関数をまったく同じ方法で処理する場合は、関数に対して行います。

混乱は、bindを既存の関数を変更するものと見なしているため、再度変更すると、元の関数が再度変更されることを期待していることだと思います。ただし、bindは何も変更せず、特定の動作を備えた新しい関数を作成します。新しい関数はそれ自体が関数であり、元の関数の魔法のパッチを適用したバージョンではありません。

そのため、なぜ標準化または発明されたのかについては謎はありません。bindは、新しいthisを提供し、引数を付加する関数を返し、すべての関数で同じように機能します。可能な限り単純なセマンティクス。

この方法でのみ実際に安全に使用できます。バインドによって既にバインドされている関数と「通常の」関数が異なる場合は、バインドする前にテストする必要があります。良い例は、新しいthisを渡すjQuery.eachです。bindがバインドされた関数を特殊化する場合、バインドされた関数をjQuery.eachに渡す安全な方法はありません。これは、呼び出しごとに上書きされるためです。これを修正するには、jQuery.eachは何らかの方法でバインドされた関数を特殊化する必要があります。

于 2013-07-25T16:24:03.440 に答える