12

任意の数の引数を指定して Javascript コンストラクターを呼び出すための解決策を探していたところ、適切な SO 投稿がいくつか見つかりました。これにより、これら 3 つの呼び出しは同じように機能するはずであると確信しました。ただし、少なくとも rhino と node.js では、次のことは行いません。

1. f = Date.bind(Date, 2000,0,1)
2. g = Date.bind.call(Date, 2000, 0, 1)
3. h = Date.bind.apply(Date, [2000, 0, 1])

最初のものは望ましい結果をもたらします:

print(new f()) //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)

しかし、他の2つはそうではありません:

print(new g()) //=> Thu Feb 01 1900 00:00:00 GMT-0500 (EST)
print(new h()) //=> Wed Jun 01 1904 00:00:00 GMT-0400 (EST)

だから、どこかで何かがおかしくなった。何についての考え?applybind、および/または のcallようなものを混ぜるのは悪い考えnewですか?

4

2 に答える 2

31

以前に受け入れられた回答は正しくありませんでした。コンストラクターで bind 、 call 、 apply を使用して、新しいコンストラクターを作成できます。テストの唯一の問題は、 bind.apply と bind.call がコンストラクター自体ではなく、 bind.apply とbind.callを適用して呼び出していることを忘れていることです。あなたは間違った議論をしました。

f = Date.bind(null, 2000,0,1)
g = Function.bind.call(Date, null, 2000, 0, 1)
h = Function.bind.apply(Date, [ null, 2000, 0, 1 ])

new f() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)
new g() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)
new h() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)

3人ともinstanceof伊達です。

Call の引数は、適用する引数が後に続く実行コンテキストです。Apply の引数は、実行コンテキストと引数の配列です。バインドの引数は、バインドする引数が後に続く実行コンテキストです。

たとえば、適用する引数は、bind (Date)を適用するコンテキストと、それに続く bind の引数 ある配列です(したがって、最初の配列メンバーは bind のコンテキスト引数です)。これが、バインドの呼び出しまたは適用が混乱する理由です。両方にコンテキスト引数を指定するのは奇妙に感じます。

コンストラクタで bind を使用する場合、'new' は明示的に新しいコンテキストを作成するため、context 引数は常に無視されることに注意してください。それを明確にするためにコンテキスト引数が無関係な場合は null を使用しますが、それは何でもかまいません。

一方、これらの例の apply と call は、bind を適用/呼び出すコンテキストが Date 関数であることを認識する必要があります。可能な場合は「日付」を「関数」に切り替えて、実際にどこでコンテキストを提供しているのかを明らかにしました。Date.bind で apply または call を呼び出すと、実際には、Date オブジェクトにアタッチされていない bind メソッドで apply または call を呼び出しています。このような場合の bind メソッドは、任意の関数から取得できます。Number.bind.call(Date, null, 2000, 0, 1) の場合でも、結果はまったく同じになります。

理由が明らかでない場合は、次の例の違いを検討してください。

context.method();

var noLongerAMethod = context.method;
noLongerAMethod();

2 番目のケースでは、メソッドは元のコンテキストから切り離されており (以前にバインドされていない限り)、内部で「this」に依存していた場合は異なる動作をします。特定の関数を直接実行するのではなく、プロパティとしてバインドをプルすると、それは単純に Function.prototype のジェネリック bind メソッドへの別のポインターになります。

個人的には、バインドを呼び出したり適用したりする必要はなかったと思います。それが適切な解決策になる状況を想像するのは難しいですが、コンストラクターをバインドして新しいコンストラクターを作成することは、時々非常に役立つことがわかりました。 . とにかく楽しいパズルです。

于 2014-04-21T04:24:49.630 に答える
4

bindおよびapply/コンストラクターではなく関数callの呼び出しを機能させるだけなので、基本的にネイティブメソッドではこれを行うことはできません。1つの方法はメソッドを作成することですが、さらに複雑になる可能性があります。bindConstruct

function bindConstruct(fn) {
    // since constructor always accepts a static this value
    // so bindConstruct cannot specify this
    var extraArgs = [].slice.call(arguments, 1);

    // create a 'subclass' of fn
    function sub() {
        var args = extraArgs.concat([].slice.call(arguments));
        fn.apply(this, args);
    }
    sub.prototype = fn.prototype;
    sub.prototype.constructor = sub;

    return sub;
}

これにより、実際には、コンストラクターのサブクラスが作成されます。

次に、コード:

var MyClass = function(x, y) {
    console.log(arguments);
    console.log(x + y);
}
var BindedMyClass = bindConstruct(MyClass, 1, 2, 3);
var c = new BindedMyClass(4, 5);
console.log(c instanceof MyClass);
console.log(c instanceof BindedMyClass);

この関数をネイティブ関数に記述しFunction.prototypeたり、ネイティブ関数の拡張として記述したりすることもできbindます。

于 2012-05-15T06:15:06.547 に答える