58

コールバック関数でオブジェクトを参照する際に、プレーンな古い JavaScript (フレームワークなし) で問題が発生しています。

function foo(id) {
    this.dom = document.getElementById(id);
    this.bar = 5;
    var self = this;
    this.dom.addEventListener("click", self.onclick, false);
}

foo.prototype = {
    onclick : function() {
        this.bar = 7;
    }
};

今、新しいオブジェクトを作成するとき (DOM がロードされた後、span#test を使用)

var x = new foo('test');

onclick 関数内の「this」は、foo オブジェクトではなく、span#test を指しています。

onclick 関数内で foo オブジェクトへの参照を取得するにはどうすればよいですか?

4

7 に答える 7

81

(他の回答のコメントに隠されていたいくつかの説明を抽出しました)

問題は次の行にあります。

this.dom.addEventListener("click", self.onclick, false);

ここでは、コールバックとして使用される関数オブジェクトを渡します。イベントがトリガーされると、関数が呼び出されますが、現在はどのオブジェクト (this) とも関連付けられていません。

この問題は、次のようにクロージャーで関数を (オブジェクト参照と共に) ラップすることで解決できます。

this.dom.addEventListener(
  "click",
  function(event) {self.onclick(event)},
  false);

クロージャーが作成されたときに変数 self がこれに割り当てられたので、クロージャー関数は後で呼び出されたときに self 変数の値を記憶します。

これを解決する別の方法は、ユーティリティ関数を作成することです (そして、thisをバインドするために変数を使用しないようにします)。

function bind(scope, fn) {
    return function () {
        fn.apply(scope, arguments);
    };
}

更新されたコードは次のようになります。

this.dom.addEventListener("click", bind(this, this.onclick), false);

Function.prototype.bindECMAScript 5 の一部であり、同じ機能を提供します。したがって、次のことができます。

this.dom.addEventListener("click", this.onclick.bind(this), false);

ES5 をまだサポートしていないブラウザーの場合、MDN は次の shim を提供します

if (!Function.prototype.bind) {  
  Function.prototype.bind = function (oThis) {  
    if (typeof this !== "function") {  
      // closest thing possible to the ECMAScript 5 internal IsCallable 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  
                                 ? this  
                                 : oThis || window,  
                               aArgs.concat(Array.prototype.slice.call(arguments)));  
        };  

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

    return fBound;  
  };  
} 
于 2008-10-11T08:33:30.163 に答える
15
this.dom.addEventListener("click", function(event) {
    self.onclick(event)
}, false);
于 2008-10-08T15:00:41.313 に答える
5

この問題の解決策を探しているjQueryユーザーの場合は、jQuery.proxyを使用する必要があります

于 2011-01-14T09:44:27.700 に答える
2

問題の良い説明(これまでに説明した解決策を理解するのに問題がありました)は、ここにあります

于 2009-11-27T17:27:43.767 に答える
2

説明はself.onclick、JavaScriptでそれが何を意味すると思うかを意味するものではないということです。これは、実際にはonclick、オブジェクトのプロトタイプ内の関数を意味しますself(それ自体を参照することはありませんself)。

JavaScriptには関数のみがあり、C#のようなデリゲートはないため、メソッドとそれを適用する必要のあるオブジェクトをコールバックとして渡すことはできません。

コールバックでメソッドを呼び出す唯一の方法は、コールバック関数内で自分でメソッドを呼び出すことです。JavaScript関数はクロージャであるため、作成されたスコープで宣言された変数にアクセスできます。

var obj = ...;
function callback(){ return obj.method() };
something.bind(callback);
于 2008-10-11T09:54:00.813 に答える
1

私はこのプラグインを書きました...

役に立つと思います

jquery.callback

于 2009-04-16T20:57:54.830 に答える
-3

これは JS の最も紛らわしい点の 1 つです。'this' 変数は最もローカルなオブジェクトを意味しますが、関数もオブジェクトなので、'this' はそこを指します。他にも微妙なところはありますが、全部は覚えていません。

私は通常、「this」の使用を避け、ローカルの「me」変数を定義して代わりに使用します。

于 2008-10-08T15:10:08.177 に答える