作成したオブジェクトに問題があり、次のようになります。
var myObject = {
AddChildRowEvents: function(row, p2) {
if(document.attachEvent) {
row.attachEvent('onclick', function(){this.DoSomething();});
} else {
row.addEventListener('click', function(){this.DoSomething();}, false);
}
},
DoSomething: function() {
this.SomethingElse(); //<-- Error here, object 'this' does not support this method.
}
}
問題は、「DoSomething」関数内にいるときに、「this」が「myObject」を参照していないことです。何が間違っているのでしょうか。
関数が呼び出されるとき、「これ」は行を指します。オブジェクトが必要な場合は、次のように実行できます。]
AddChildRowEvents: function(row, p2) {
var theObj = this;
if(document.attachEvent) {
row.attachEvent('onclick', function(){theObj.DoSomething();});
} else {
row.addEventListener('click', function(){theObj.DoSomething();}, false);
}
},
関数が呼び出されると、関数が定義されたときにスコープ内にあった変数theOBjにアクセスできます。
this
は常に内部関数を参照します。ネストされた関数がある場合は、別の変数を作成し、それをthis
にポイントする必要があります。
var myObject = {
AddChildRowEvents: function(row, p2) {
var that = this;
if(document.attachEvent) {
row.attachEvent('onclick', function(){that.DoSomething();});
} else {
row.addEventListener('click', function(){that.DoSomething();}, false);
}
}
}
問題は、JSでthis
がどのように管理されているかに起因します。特に、コールバックが非同期で呼び出された場合(およびクロージャが作成された場合は、これにバインドされます)、迅速に処理できます。
AddChildRowEventsメソッドが呼び出されると、this
は変数myObject
を適切に参照します。ただし、このメソッドの目的は、クリック用のハンドラーを配置することです。そのため、イベントは将来いつかトリガーされます。そのとき、どのオブジェクトが実際にイベントをトリガーしますか?実際、ユーザーがクリックするのはDOM element
です。したがって、this
はこのDOM element
を参照し、myObject
変数は参照しません。
提示されているものよりも最新のソリューションについては、bind method
またはarrow function
を使用できます。
let handler = {
addEventHandler: function(row) {
console.log("this", this)
row.addEventListener("click", () => {
console.log("this", this)
this.doSomethingElse()
})
},
doSomethingElse: function() {
console.log("something else")
}
}
var div = document.querySelector("div")
handler.addEventHandler(div)
<div>one</div>
arrow function
(ES2015
以降で使用可能)を使用すると、実行コンテキストではなく字句コンテキストでthis
コンテキストが使用されます。 2つの違いは、字句コンテキストは実行時ではなくビルド時に検出されるため、字句コンテキストの追跡が容易になることです。ここで、arrow function
内では、this
は外部関数の同じthis
(つまり、addEventHandler
)を参照するため、myObject
を参照します。
ファットアロー関数と通常の関数のプロパティの詳細については、次を参照してください。 https://dmitripavlutin.com/6-ways-to-declare-javascript-functions/
let handler = {
addEventHandler: function(row) {
console.log("this", this)
row.addEventListener("click", function() {
console.log("this", this)
this.doSomethingElse()
}.bind(this))
},
doSomethingElse: function() {
console.log("something else")
}
}
var div = document.querySelector("div")
handler.addEventHandler(div)
<div>one</div>
今回、コールバック関数がaddEventListener
に渡されると、this
コンテキストが外部のthis
関数のaddEventHandler
要素にバインドされます。
これはクロージャの一般的な問題です。それを解決するには、次のようなものを試してください。
var myObject = {
AddChildRowEvents: function(row, p2) {
var self = this;
if(document.attachEvent) {
row.attachEvent('onclick', function(){this.DoSomething(self);});
} else {
row.addEventListener('click', function(){this.DoSomething(self);}, false);
}
},
DoSomething: function(self) {
self.SomethingElse();
}
}
コードの問題はここにあります。次のコード行で無名関数を定義し、それをonClickイベントのイベントハンドラーとして次の行に渡しました。
row.attachEvent('onclick', function(){this.DoSomething();});
そして
row.addEventListener('click', function(){this.DoSomething();}, false);
onclickイベントが発生し、渡した匿名関数が呼び出されると、thisコンテキストはイベントが発生したオブジェクトを参照しますが、myObjectは参照しません。この時点で、実行中のコンテキストはイベントハンドラーコンテキストにあるためです。
解決策:Mike Kantorに言われたトリックを実行するか、jQueryを使用して、プロキシメソッドを使用して無名関数でthisコンテキストを定義できます。
したがって、コードは次のようになります。
var myObject = {
AddChildRowEvents: function(row, p2) {
if(document.attachEvent) {
row.attachEvent('onclick', $.proxy(function () {
this.DoSomething();
}, this));
} else {
row.addEventListener('click', $.proxy(function () {
this.DoSomething();
},this), false);
}
},
DoSomething: function() {
this.SomethingElse(); //<-- Error here, object 'this' does not support this method.
}
}
エラーがthis.SomthingElse()にあるとおっしゃいましたが、コードに表示されません。そのコード行で実際にエラーが発生した場合は、メソッドDoSomthingをイベントハンドラーとして使用している可能性があります。