イベントハンドラを登録するコンストラクタ関数があります。
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', function () {
alert(this.data);
});
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
しかし、コールバック内で作成したオブジェクトのdata
プロパティにアクセスすることはできません。 this
は作成されたオブジェクトではなく、他のオブジェクトを参照しているように見えます。
私はまた無名関数の代わりにオブジェクトメソッドを使用しようとしました:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', this.alert);
}
MyConstructor.prototype.alert = function() {
alert(this.name);
};
しかし、それは同じ問題を示します。
正しいオブジェクトにアクセスする方法
this
について知っておくべきことthis
(別名「コンテキスト」)は各関数内の特別なキーワードであり、その値はhow関数が定義された方法/時/場所ではなく、関数の呼び出しにのみ依存します。他の変数のような字句スコープの影響を受けません(矢印関数を除く、以下を参照)。ここではいくつかの例を示します。
function foo() {
console.log(this);
}
// normal function call
foo(); // `this` will refer to `window`
// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`
// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`
this
の詳細については、 MDN documentation をご覧ください。
this
を参照する方法this
を使用しないでください実際には、特にthis
にアクセスする必要はありませんが、参照するオブジェクト。そのため、簡単な解決策は、そのオブジェクトも参照する新しい変数を単に作成することです。変数には任意の名前を付けることができますが、一般的なものはself
およびthat
です。
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
self
は通常の変数であるため、字句スコープの規則に従い、コールバック内からアクセスできます。これには、コールバック自体のthis
値にアクセスできるという利点もあります。
this
を明示的に設定する-パート1this
の値は自動的に設定されるため、値を制御できないように見えるかもしれませんが、実際はそうではありません。
すべての関数にはメソッドがあります .bind
[ドキュメント] 、値にバインドされたthis
を持つ新しい関数を返します。この関数の動作は、.bind
で呼び出したものとまったく同じですが、this
が設定されているだけです。その関数がいつどのように呼び出されても、this
は常に渡された値を参照します。
function MyConstructor(data, transport) {
this.data = data;
var boundFunction = (function() { // parenthesis are not necessary
alert(this.data); // but might improve readability
}).bind(this); // <- here we are calling `.bind()`
transport.on('data', boundFunction);
}
この場合、コールバックのthis
をMyConstructor
のthis
の値にバインドしています。
注: jQueryのコンテキストをバインドするときは、 jQuery.proxy
を使用します[ドキュメント] 代わりに。これを行う理由は、イベントコールバックのバインドを解除するときに関数への参照を保存する必要がないようにするためです。 jQueryはそれを内部的に処理します。
ECMAScript 6にはarrow関数が導入されています。これはラムダ関数と考えることができます。独自のthis
バインディングはありません。代わりに、this
は通常の変数と同様にスコープ内で検索されます。つまり、.bind
を呼び出す必要はありません。それは彼らが持っている唯一の特別な動作ではありません。詳細についてはMDNのドキュメントを参照してください。
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
this
を設定する-パート2コールバックを受け入れる一部の関数/メソッドは、コールバックのthis
が参照する必要がある値も受け入れます。これは基本的に自分でバインドするのと同じですが、関数/メソッドが自動的にバインドします。 Array#map
[ドキュメント] はそのような方法です。その署名は次のとおりです。
array.map(callback[, thisArg])
最初の引数はコールバックであり、2番目の引数はthis
が参照する値です。これが不自然な例です:
var arr = [1, 2, 3];
var obj = {multiplier: 42};
var new_arr = arr.map(function(v) {
return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument
注:this
に値を渡すことができるかどうかは、通常、その関数/メソッドのドキュメントに記載されています。たとえば、 jQueryの$.ajax
メソッド[ドキュメント] はcontext
と呼ばれるオプションを説明します:
このオブジェクトは、すべてのAjax関連のコールバックのコンテキストになります。
この問題のもう1つの一般的な症状は、オブジェクトメソッドがコールバック/イベントハンドラーとして使用される場合です。関数はJavaScriptの第一級市民であり、「メソッド」という用語は、オブジェクトプロパティの値である関数の口語的な用語です。ただし、その関数には、その「包含」オブジェクトへの特定のリンクはありません。
次の例を考えてみましょう。
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = function() {
console.log(this.data);
};
関数this.method
はクリックイベントハンドラとして割り当てられますが、document.body
がクリックされると、記録される値はundefined
になります。イベントハンドラ内では、this
がdocument.body
ではなくFoo
のインスタンス。
冒頭ですでに述べたように、this
が参照するものは、関数がdefinedではなく、calledである方法に依存します。
コードが次のようなものである場合、関数がオブジェクトへの暗黙的な参照を持たないことがより明白になる可能性があります。
function method() {
console.log(this.data);
}
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = method;
解決策は上記と同じです:可能な場合は、.bind
を使用してthis
を特定の値に明示的にバインドします
document.body.onclick = this.method.bind(this);
または、コールバック/イベントハンドラーとして匿名関数を使用し、オブジェクト(this
)を別の変数に割り当てることにより、関数をオブジェクトの「メソッド」として明示的に呼び出します。
var self = this;
document.body.onclick = function() {
self.method();
};
または、矢印関数を使用します。
document.body.onclick = () => this.method();
bind()
関数を使うことができます。 bind()
関数を使うfunction MyConstructor(data, transport) {
this.data = data;
transport.on('data', ( function () {
alert(this.data);
}).bind(this) );
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
underscore.js
を使用している場合 - http://underscorejs.org/#bind
transport.on('data', _.bind(function () {
alert(this.data);
}, this));
function MyConstructor(data, transport) {
var self = this;
this.data = data;
transport.on('data', function() {
alert(self.data);
});
}
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
メソッドを呼び出すのは「魔法の」構文です。
object.property();
オブジェクトからプロパティを取得して一度に呼び出すと、そのオブジェクトがメソッドのコンテキストになります。同じメソッドを別の手順で呼び出す場合は、代わりにコンテキストがグローバルスコープ(ウィンドウ)になります。
var f = object.property;
f();
メソッドの参照を取得すると、そのメソッドはオブジェクトにアタッチされなくなり、単なる関数への参照になります。コールバックとして使用する参照を取得したときにも同じことが起こります。
this.saveNextLevelData(this.setAll);
そこでコンテキストを関数にバインドします。
this.saveNextLevelData(this.setAll.bind(this));
JQueryを使用している場合は、すべてのブラウザでbind
がサポートされているわけではないので、代わりに$.proxy
メソッドを使用してください。
this.saveNextLevelData($.proxy(this.setAll, this));
「コンテキスト」という用語は、 this で参照されるオブジェクトを指すのに使用されることがあります。意味的にも技術的にも ECMAScriptの this には適合しないため、その使用は不適切です。
"Context" は、意味を追加するもの、または追加の意味を与える前後の情報を取り巻く状況を意味します。 "context"という用語はECMAScriptで 実行コンテキスト を指すために使用されます。これは、実行コードの範囲内で、すべてのパラメータ、スコープ、および this です。
これは ECMA-262の10.4.2節に示されています :
ThisBindingを呼び出し実行コンテキストのThisBindingと同じ値に設定します。
これは、 this が実行コンテキストの一部であることを明確に示しています。
実行コンテキストは、実行されているコードに意味を追加する周囲の情報を提供します。これには、 thisBinding というより多くの情報が含まれています。
したがって、 this の値は「コンテキスト」ではなく、実行コンテキストの一部に過ぎません。これは基本的にローカル変数で、任意のオブジェクトへの呼び出しと厳密モードでは、任意の値に設定できます。
まず、scope
NAME_を明確に理解し、this
NAME_のコンテキストでscope
NAME_キーワードの動作を理解する必要があります。
this
name__&scope
name__:
there are two types of scope in javascript. They are :
1) Global Scope
2) Function Scope
要するに、グローバルスコープはウィンドウオブジェクトを指します。グローバルスコープで宣言された変数はどこからでもアクセスできます。一方、関数スコープは関数内にあります。関数内で宣言された変数は外部から通常アクセスできません。this
NAME_グローバルスコープのキーワードはウィンドウオブジェクトを参照します。this
NAME_関数内もウィンドウオブジェクトを参照します。したがって、this
NAME_は、方法を見つけるまで常にウィンドウを参照します。 this
NAME_を操作して、自分で選択したコンテキストを示します。
--------------------------------------------------------------------------------
- -
- Global Scope -
- ( globally "this" refers to window object) -
- -
- function outer_function(callback){ -
- -
- // outer function scope -
- // inside outer function"this" keyword refers to window object - -
- callback() // "this" inside callback also refers window object -
- } -
- -
- function callback_function(){ -
- -
- // function to be passed as callback -
- -
- // here "THIS" refers to window object also -
- -
- } -
- -
- outer_function(callback_function) -
- // invoke with callback -
--------------------------------------------------------------------------------
コールバック関数内でthis
name__を操作するさまざまな方法:
ここには、Personというコンストラクター関数があります。 name
NAME_というプロパティと、sayNameVersion1
、sayNameVersion2
、sayNameVersion3
、sayNameVersion4
という4つのメソッドがあります。 4つすべてに特定のタスクが1つあります。コールバックを受け入れて呼び出します。コールバックには、Personコンストラクター関数のインスタンスのnameプロパティを記録する特定のタスクがあります。
function Person(name){
this.name = name
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
this.sayNameVersion3 = function(callback){
callback.call(this)
}
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
}
function niceCallback(){
// function to be used as callback
var parentObject = this
console.log(parentObject)
}
では、Personコンストラクターからインスタンスを作成し、sayNameVersionX
NAME_(Xは1,2,3,4を指します)メソッドのさまざまなバージョンをniceCallback
NAME_で呼び出して、 this
NAME_インスタンスを参照するコールバック内のperson
NAME_.
var p1 = new Person('zami') // create an instance of Person constructor
バインドは、指定された値にthis
NAME_キーワードを設定して新しい関数を作成します。
sayNameVersion1
およびsayNameVersion2
はバインドを使用して、コールバック関数のthis
NAME_を操作します。
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
最初のメソッドは、メソッド自体の内部でコールバックを使用してthis
NAME_をバインドします。2番目のコールバックは、オブジェクトにバインドされた状態で渡されます。
p1.sayNameVersion1(niceCallback) // pass simply the callback and bind happens inside the sayNameVersion1 method
p1.sayNameVersion2(niceCallback.bind(p1)) // uses bind before passing callback
call
NAME_メソッドのfirst argument
は、this
NAME_を付けて呼び出された関数内でcall
NAME_として使用されます。
sayNameVersion3
はcall
NAME_を使用してthis
NAME_を操作し、ウィンドウオブジェクトではなく、作成した人物オブジェクトを参照します。
this.sayNameVersion3 = function(callback){
callback.call(this)
}
そして、それは次のように呼び出されます:
p1.sayNameVersion3(niceCallback)
call
NAME_と同様に、apply
NAME_の最初の引数は、this
NAME_キーワードで示されるオブジェクトを参照します。
sayNameVersion4
はapply
NAME_を使用してthis
NAME_を操作し、人物オブジェクトを参照します
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
次のように呼び出されます。単にコールバックが渡されますが、
p1.sayNameVersion4(niceCallback)
これをsetTimeout()
にバインドすることはできません。コールバック関数でthis
コンテキストにアクセスしたい場合は、bind()
をコールバック関数に使用することで、 global object(Window) で実行できます。
setTimeout(function(){
this.methodName();
}.bind(this), 2000);
"this"キーワードについて知っておくべきです。
私の見方では、3つの方法で "this"を実装することができます (自己/矢印関数/バインド方法)
関数のthisキーワードは、JavaScriptでは他の言語と比べて少し異なる動作をします。
厳密モードと非厳密モードの間にもいくつかの違いがあります。
ほとんどの場合、この値は関数の呼び出し方法によって決まります。
実行中の代入で設定することはできず、関数が呼び出されるたびに異なる可能性があります。
ES5では、呼び出し方法に関係なく、関数のthisの値を設定するためにbind()メソッドが導入されました。
そしてES2015は独自のthisバインディングを提供しないarrow関数を導入しました(それは包含している字句コンテキストのthis値を保持します)。
方法1: Self - コンテキストが変わっても、Selfは元のthisへの参照を維持するために使用されています。これはイベントハンドラ(特にクロージャ)でよく使われる手法です。
参照 : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function () {
alert(self.data);
});
}
Method2 :矢印関数 - 矢印関数式は、通常の関数式に代わる構文上コンパクトなものです。
ただし、this、arguments、super、またはnew.targetキーワードに対する独自のバインディングはありません。
矢印関数式はメソッドとしては適していません。コンストラクタとしては使用できません。
参照 : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',()=> {
alert(this.data);
});
}
Method3 :Bind- bind()メソッドは新しい関数を作成します。
呼び出されると、thisキーワードが指定された値に設定されます。
新しい関数が呼び出されるときに提供されるものに先行する引数の与えられたシーケンスで。
参照: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',(function() {
alert(this.data);
}).bind(this);
DOM2 からイベントリスナー内でthis
をバインドするための標準的な方法、 (他の利点もありますが)リスナー を削除させる方法は、EventListener
インターフェースからhandleEvent(evt)
メソッドです。 :
var obj = {
handleEvent(e) {
// always true
console.log(this === obj);
}
};
document.body.addEventListener('click', obj);
handleEvent
の使用に関する詳細な情報はここにあります: https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38
現在、クラスがコード内で使用されている場合、別のアプローチが可能です。
クラスフィールドをサポートすることで それは次のようにすることが可能です:
class someView {
onSomeInputKeyUp = (event) => {
console.log(this); // this refers to correct value
// ....
someInitMethod() {
//...
someInput.addEventListener('input', this.onSomeInputKeyUp)
確かに内部的には、文脈を束縛するのは古き良き矢印関数ですが、この形式では明示的束縛をもっと明確にしています。
Stage 3 Proposalだから、今のところ処理するにはbabelと適切な babelプラグイン が必要になるだろう(08/2018)。
質問はjavascriptでthis
キーワードがどのように振る舞うかを中心に展開します。 this
は以下のように動作が異なります。
答えの大部分が示唆するように、 矢印関数またはbind()
メソッドまたはSelf varを使用できます。私は Google JavaScript Style Guide からラムダ(矢印関数)についてのポイントを引用するでしょう
F.bind(this)より、特に goog.bind(f、this)よりも矢印関数を使用することをお勧めします。 const self = thisと書かないでください。矢印関数は、コールバックに特に役立ちます。コールバックでは、予期しない追加の引数を渡すことがあります。
バインドやconst self = this
よりもラムダを使用することをお勧めします
したがって、最善の解決策は、以下のようにラムダを使用することです。
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
参考文献: