35

一部の Javascript コード (具体的には node.js) では、コンテキストを変更せずに未知の引数セットで関数を呼び出す必要があります。例えば:

function fn() {
    var args = Array.prototype.slice.call(arguments);
    otherFn.apply(this, args);
}

上記の問題は、 を呼び出すときに、最初の引数としてapply渡すことでコンテキストを変更していることです。呼び出される関数のコンテキストを変更せずに、呼び出される関数thisに渡したいと思います。私は本質的にこれをしたい:args

function fn() {
    var args = Array.prototype.slice.call(arguments);
    otherFn.apply(<otherFn's original context>, args);
}

編集:私の特定の質問に関する詳細を追加します。接続に関する他の情報に加えて、ソケット (socket.io) オブジェクトを含む Client クラスを作成しています。クライアント オブジェクト自体を介してソケットのイベント リスナーを公開しています。

class Client
  constructor: (socket) ->
    @socket    = socket
    @avatar    = socket.handshake.avatar
    @listeners = {}

  addListener: (name, handler) ->
    @listeners[name] ||= {}
    @listeners[name][handler.clientListenerId] = wrapper = =>
      # append client object as the first argument before passing to handler
      args = Array.prototype.slice.call(arguments)
      args.unshift(this)
      handler.apply(this, args)  # <---- HANDLER'S CONTEXT IS CHANGING HERE :(

    @socket.addListener(name, wrapper)

  removeListener: (name, handler) ->
    try
      obj = @listeners[name]
      @socket.removeListener(obj[handler.clientListenerId])
      delete obj[handler.clientListenerId]

ここにある回答clientListenerIdと本質的に同じカスタムの一意の識別子プロパティであることに注意してください。

4

10 に答える 10

6

' this'、関数のコンテキストへの参照です。それが本当にポイントです。

次のような別のオブジェクトのコンテキストで呼び出す場合:

otherObj.otherFn(args)

次に、そのオブジェクトをコンテキストに置き換えます。

otherObj.otherFn.apply(otherObj, args);

それだけです。

于 2012-09-09T02:28:18.540 に答える
1

関数が呼び出されたときに JavaScript で発生する可能性があるコンテキストの変更を回避する 1 つの方法は、オブジェクトのコンストラクターの一部であるメソッドを使用することですthis。元のthis識別子を格納するためのローカル プライベート変数を効果的に作成することにより、親オブジェクト。

JavaScript のスコープに関するほとんどの議論と同様に、これは完全に明確ではないことを認めます。そのため、これをどのように行ったかの例を次に示します。

function CounterType()
{
    var counter=1;
    var self=this; // 'self' will now be visible to all

    var incrementCount = function()
    {
        // it doesn't matter that 'this' has changed because 'self' now points to CounterType()
        self.counter++;
    };

}

function SecondaryType()
{
    var myCounter = new CounterType();
    console.log("First Counter : "+myCounter.counter); // 0
    myCounter.incrementCount.apply(this); 
    console.log("Second Counter: "+myCounter.counter); // 1
}
于 2014-02-21T00:30:58.873 に答える
0

bindJavascript 1.8.5で定義されている関数を使用しthis、bind関数を渡した元のオブジェクトを取得できるようにしたい場合は、Function.prototype.bind関数を再定義することをお勧めします。

Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
        throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var aArgs = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        fNOP = function () {},
        fBound = function () {
            return fToBind.apply(this instanceof fNOP && oThis
            ? this
            : oThis,
            aArgs.concat(Array.prototype.slice.call(arguments)));
        };

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    /** here's the additional code **/
    fBound.getContext = function() {
        return oThis;
    };
    /**/

    return fBound;
};

bindこれで、関数を呼び出した元のコンテキストを取得できます。

function A() {
    return this.foo+' '+this.bar;
}

var HelloWorld = A.bind({
    foo: 'hello',
    bar: 'world',
});

HelloWorld(); // returns "hello world";
HelloWorld.getContext(); // returns {foo:"hello", bar:"world"};
于 2012-09-10T22:27:44.547 に答える
0

私はまだもっと適切なものを望んでいるので、これを答えとして受け入れるつもりはありません。しかし、これまでのこの質問に対するフィードバックに基づいて、私が現在使用しているアプローチを次に示します。

Client.prototype.addListenerorを呼び出すすべてのクラスについてClient.prototype.removeListener、コンストラクターに次のコードを追加しました。

class ExampleClass
  constructor: ->
    # ...
    for name, fn of this
      this[name] = fn.bind(this) if typeof(fn) == 'function'

   message: (recipient, body) ->
     # ...

   broadcast: (body) ->
     # ...

上記の例では、インスタンス化されると常に新しいプロトタイプ オブジェクトにバインドされ、元の質問のmessageコードbroadcastが機能します。ExampleClassaddListener

なぜ私が次のようなことをしなかったのか疑問に思っている人もいると思います。

example = new ExampleClass
client.addListener('message', example.bind(example))
# ...
client.removeListener('message', example.bind(example))

問題は、呼び出されるたび.bind( )に新しいオブジェクトになることです。つまり、次のことが当てはまります。

example.bind(example) != example.bind(example)

そのため、removeListenerが正常に機能することはないため、オブジェクトがインスタンス化されたときにメソッドを 1 回バインドします。

于 2012-09-09T12:40:40.693 に答える
-1

プロパティを関数のオブジェクトに直接プッシュし、独自の「コンテキスト」で呼び出すだけです。

function otherFn() {
    console.log(this.foo+' '+this.bar); // prints: "hello world" when called from rootFn()
}

otherFn.foo = 'hello';
otherFn.bar = 'world';

function rootFn() {
    // by the way, unless you are removing or adding elements to 'arguments',
    // just pass the arguments object directly instead of casting it to Array
    otherFn.apply(otherFn, arguments);
}
于 2012-09-09T09:33:06.600 に答える