JavaScript:これはどういう意味ですか?
JavaScriptでは this
の値を理解するのに困る場合がありますが、次のとおりにやってみましょう
JavaScriptのthis
は笑いネタであり、それはまあ、それはかなり複雑からです。しかし、開発者がthis
対処を避けるために、もっと複雑かつドメイン固有のものをしたことを見られました 。this
に困れば、この記事が役に立つかもしれません。これが私のthis
についてのガイドです。
最も具体的な状況から始めて、最も具体的でない状況で終わります。この記事は、if (…) … else if () … else if (…) …
ように大きいものなので、探しているコードに一致する最初のセクションに直接進むことができます。
- 関数が矢印関数として定義されている場合
- それ以外、関数/クラスが
new
で呼び出された場合 - それ以外、関数に「バインドされた」
this
値がある場合 - それ以外、
this
がcall-timeに設定されている場合 - それ以外、関数が親オブジェクト(
parent.func()
)を介して呼び出された場合 - それ以外、関数または親スコープが厳密モードにある場合
- それ以外の場合
関数が矢印関数として定義されている場合: #
const arrowFunction = () => {
console.log(this);
};
この場合には、this
の値は常に親スコープ内にあるthis
と同じです:
const outerThis = this;
const arrowFunction = () => {
// Always logs `true`:
console.log(this === outerThis);
};
矢印関数ではthis
の内側の値が変更されなくて、優れるものです。 常に外側のthis
と同じです。
その他の例 #
矢印関数の使用により、this
の値がbind
に変更されません :
// Logs `true` - bound `this` value is ignored:
arrowFunction.bind({foo: 'bar'})();
矢印関数の使用により、this
の値がcall
またはapply
に変更されません :
// Logs `true` - called `this` value is ignored:
arrowFunction.call({foo: 'bar'});
// Logs `true` - applied `this` value is ignored:
arrowFunction.apply({foo: 'bar'});
矢印関数では、別のオブジェクトのメンバーとして関数を呼び出しても、this
の値が変更されません。
const obj = {arrowFunction};
// Logs `true` - parent object is ignored:
obj.arrowFunction();
矢印関数では、コンストラクターとして関数を呼び出しても、this
値が変更されません。
// TypeError: arrowFunction is not a constructor
new arrowFunction();
「バインドされた」インスタンスメソッド #
インスタンスメソッドでは、this
が常にクラスインスタンスを参照することを確保したい場合は、最善の方法として矢印関数とclass fieldsを使用することを勧めます。
class Whatever {
someMethod = () => {
// Always the instance of Whatever:
console.log(this);
};
}
このパターンは、コンポーネント(ReactコンポーネントやWebコンポーネントなど)でイベントリスナーとしてインスタンスメソッドを使用する上で非常に役立ちます。
上記は、「this
は親スコープの this
と同じになる」というルールに違反しているように感じるかもしれませんが、クラスフィールドをコンストラクターで設定するための糖衣構文のようなものと考えると意味が分かります。
class Whatever {
someMethod = (() => {
const outerThis = this;
return () => {
// Always logs `true`:
console.log(this === outerThis);
};
})();
}
// …is roughly equivalent to:
class Whatever {
constructor() {
const outerThis = this;
this.someMethod = () => {
// Always logs `true`:
console.log(this === outerThis);
};
}
}
代替パッテンには、コンストラクターで既存関数をバインドするか、コンストラクター内で関数を割り当てることが含まれます。何らかの理由でクラスフィールドを使用できない場合は、代替手段としてコンストラクター内で関数を割り当てることをお勧めします。
class Whatever {
constructor() {
this.someMethod = () => {
// …
};
}
}
それ以外、関数/クラスがnew
: で呼び出された場合 #
new Whatever();
下記のようにthis
で Whatever
(またはクラスの場合はそのconstructor関数)を呼び出し、Object.create(Whatever.prototype)
の結果に設定します。
class MyClass {
constructor() {
console.log(
this.constructor === Object.create(MyClass.prototype).constructor,
);
}
}
// Logs `true`:
new MyClass();
同様なやり方は古いスタイルのconstructorにも当てはまります。
function MyClass() {
console.log(
this.constructor === Object.create(MyClass.prototype).constructor,
);
}
// Logs `true`:
new MyClass();
その他の例 #
new
で呼び出された場合、 this
はbind
で変更されません:
const BoundMyClass = MyClass.bind({foo: 'bar'});
// Logs `true` - bound `this` value is ignored:
new BoundMyClass();
new
で呼び出された場合、別のオブジェクトのメンバーとして関数を呼び出しても、this
の値が変更されません。
const obj = {MyClass};
// Logs `true` - parent object is ignored:
new obj.MyClass();
それ以外、関数に「バウンドされた」this
の値がある場合 : #
function someFunction() {
return this;
}
const boundObject = {hello: 'world'};
const boundFunction = someFunction.bind(boundObject);
boundFunction
呼ばれるたびに、そのthis
値はbind
(boundObject
)に渡されたオブジェクトになります。
// Logs `false`:
console.log(someFunction() === boundObject);
// Logs `true`:
console.log(boundFunction() === boundObject);
その他の例 #
バインドされた関数を呼び出す場合、this
の値がcall
またはapply
に変更されません :
// Logs `true` - called `this` value is ignored:
console.log(boundFunction.call({foo: 'bar'}) === boundObject);
// Logs `true` - applied `this` value is ignored:
console.log(boundFunction.apply({foo: 'bar'}) === boundObject);
バインドされた関数を呼び出す場合、別のオブジェクトのメンバーとして関数を呼び出してもthis
値が変更されません。
const obj = {boundFunction};
// Logs `true` - parent object is ignored:
console.log(obj.boundFunction() === boundObject);
それ以外、 this
がcall-timeに設定されている場合: #
function someFunction() {
return this;
}
const someObject = {hello: 'world'};
// Logs `true`:
console.log(someFunction.call(someObject) === someObject);
// Logs `true`:
console.log(someFunction.apply(someObject) === someObject);
this
の値にcall
/apply
に渡されたオブジェクトです 。
残念ながら、 this
はDOMイベントリスナーなどによって他の値に設定されており、これを使用すると、コードが理解しにくくなる可能性があります。
してはいけないこと
element.addEventListener('click', function (event) {
// Logs `element`, since the DOM spec sets `this` to
// the element the handler is attached to.
console.log(this);
});
上記の場合などにはthis
を使用せず、代わりに次のようにします。
すべきこと
element.addEventListener('click', (event) => {
// Ideally, grab it from a parent scope:
console.log(element);
// But if you can't do that, get it from the event object:
console.log(event.currentTarget);
});
それ以外、関数が親オブジェクト (parent.func()
)を介して呼び出された場合: #
const obj = {
someMethod() {
return this;
},
};
// Logs `true`:
console.log(obj.someMethod() === obj);
この場合、関数はobj
のメンバーとして呼び出されるため、 this
はobj
になります。これはcall-timeに発生するため、関数が親オブジェクトなしで、または別の親オブジェクトを使用して呼び出された場合、リンクは切断されます。
const {someMethod} = obj;
// Logs `false`:
console.log(someMethod() === obj);
const anotherObj = {someMethod};
// Logs `false`:
console.log(anotherObj.someMethod() === obj);
// Logs `true`:
console.log(anotherObj.someMethod() === anotherObj);
someMethod
はobj
メンバーとして呼び出されないため、someMethod() === obj
はfalseになります。次のようなことを試みたときに、この落とし穴に遭遇するかもしれません。
const $ = document.querySelector;
// TypeError: Illegal invocation
const el = $('.some-element');
これは、 querySelector
の実装がそれ自体のthis
値を調べて、それが一種のDOMノードであると想定するために壊れ、そしてその接続を壊します。上記を正しく機能するには:
const $ = document.querySelector.bind(document);
// Or:
const $ = (...args) => document.querySelector(...args);
面白い事実:すべてのAPIがthis
内部で使用しているわけではありません。console.log
などのコンソールメソッドはthis
参考を回避するように変更されたため、log
はconsole
にバインドされる必要はありません。
それ以外、関数または親スコープが厳密モードにある場合: #
function someFunction() {
'use strict';
return this;
}
// Logs `true`:
console.log(someFunction() === undefined);
この場合には、this
の値は未定義です。親スコープが厳密モードにある場合(およびすべてのモジュールが厳密モードにある場合)、'use strict'
は必要ありません。
それ以外: #
function someFunction() {
return this;
}
// Logs `true`:
console.log(someFunction() === globalThis);
この場合には、this
の値はglobalThis
と同じです 。
ふぅ! #
以上です!それがthis
について把握しているのです。何かご質問はありませんか?見逃したものはありますか?お気軽にツイートしてください。
Mathias Bynens 、Ingvar Stepanyan 、とThomas Steinerに見直していただき、誠にありがとうございます。