「this」キーワードの機能と正しい使用方法について明確な説明を探しています。
動作がおかしいようですが、その理由がよくわかりません。
どのようthis
に機能し、いつ使用すればよいのでしょうか?
ベストアンサー1
this
は、実行コンテキストのプロパティである JavaScript のキーワードです。主に関数とコンストラクターで使用されます。のルールはthis
非常に単純です (ベスト プラクティスに従う場合)。
this
仕様書の技術的説明
のECMAScript 標準定義するthis
抽象操作(略称AO)を介してこのバインディングを解決する:
this
[AO] ResolveThisBinding […]は、LexicalEnvironmentを使用してキーワードのバインディングを決定します。実行コンテキストの実行[手順]:
- envRecをこの環境を取得する()。
- 戻り値: envRec .GetThisBinding()。
地球環境記録、モジュール環境記録、 そして機能環境レコードそれぞれ独自の GetThisBinding メソッドがあります。
のこの環境を取得するAOは現在の実行コンテキストの実行の LexicalEnvironment を調べ、thisバインディングを持つ(つまり HasThisBinding がtrueを返す)最も近い上位の環境レコードを検索します([[OuterEnv]] プロパティに繰り返しアクセスします)。このプロセスは、3 つの環境レコード タイプのいずれかで終了します。
の価値はthis
、コードが厳密モード。
GetThisBinding の戻り値は現在の実行コンテキストの値を反映するためthis
、新しい実行コンテキストが確立されるたびに、this
異なる値に解決されます。これは、現在の実行コンテキストが変更された場合にも発生する可能性があります。次のサブセクションでは、これが発生する可能性がある 5 つのケースを示します。
コードサンプルはAST エクスプローラー仕様の詳細に従ってください。
1. スクリプト内のグローバル実行コンテキスト
これはトップレベル、つまり 内で直接評価されるスクリプト コードです<script>
。
<script>
// Global context
console.log(this); // Logs global object.
setTimeout(function(){
console.log("Not global context");
});
</script>
スクリプトの初期のグローバル実行コンテキストでは、評価this
によってGetThisBinding以下の手順を実行します。
グローバル環境レコードenvRecの GetThisBinding 具象メソッドは[…] [次の処理を実行します]:
- envRec .[[GlobalThisValue]]を返します。
グローバル環境レコードの[[GlobalThisValue]]プロパティは常にホスト定義に設定されます。グローバルオブジェクト、これは以下からアクセス可能ですglobalThis
( window
Web 上、global
Node.js 上;MDN のドキュメント)。以下の手順に従ってください。ホスト定義領域の初期化[[GlobalThisValue]] プロパティがどのように生成されるかを学びます。
2. グローバル実行コンテキストモジュール
モジュールは ECMAScript 2015 で導入されました。
<script type="module">
これは、単純な ではなく、の直接内部にある場合など、モジュールに適用されます<script>
。
モジュールの初期グローバル実行コンテキストでは、評価this
によってGetThisBinding以下の手順を実行します。
モジュール Environment Record の GetThisBinding 具象メソッドは、次の処理を実行します。
- undefinedを返します。
モジュールでは、 の値はthis
常にundefined
グローバルコンテキストにあります。モジュールは暗黙的に厳密モード。
3. 入力評価コード
呼び出しには 2 種類ありますeval
。直接そして間接的この区別は ECMAScript 第 5 版から存在します。
- 直接呼び出しは通常、 …または… (または…など)
eval
のようになります。1呼び出し式が狭いパターンに適合する場合にのみ直接呼び出しになります。2eval(
);
(eval)(
);
((eval))(
);
- 間接
eval
呼び出しでは、関数参照をeval
他の方法で呼び出します。eval?.(
…)
、(
…, eval)(
…)
、window.eval(
…)
、eval.call(
…,
…)
などが考えられます。 の場合、 … 、…const aliasEval1 = eval; window.aliasEval2 = eval;
にもなります。 また、 の場合、 …を呼び出すことも間接的になります。aliasEval1(
)
aliasEval2(
)
const originalEval = eval; window.eval = (x) => originalEval(x);
eval(
)
見るchuckj の回答「JavaScript での (1, eval)('this') と eval('this') の違いは?」そしてDmitry Soshnikov の ECMA-262-5 の詳細 – 第 2 章: 厳密モード(アーカイブ済み) は、間接eval()
呼び出しを使用する場合に使用します。
実行評価コードを実行しますeval
。新しい宣言的環境記録LexicalEnvironmentとして、この環境を取得するから値を取得しますthis
。
次に、コードthis
内に出現した場合eval
、環境レコードのGetThisBindingメソッドは、この環境を取得するが呼び出され、その値が返されます。
そして創造された宣言的環境記録eval
通話が直接か間接かによって異なります。
- 直接評価では、現在の実行コンテキストの実行の LexicalEnvironment。
- 間接的な評価では、[[GlobalEnv]]プロパティ(地球環境記録)のレルムレコード間接的な評価を実行しました。
つまり、
- 直接的な eval では、
this
値は変更されず、 を呼び出したレキシカル スコープから取得されますeval
。 - 間接的な評価では、
this
値はグローバル オブジェクト (globalThis
) です。
についてはどうnew Function
ですか ?new Function
は に似ていますeval
が、コードをすぐに呼び出さず、関数を作成します。このバインディングは、関数が呼び出される場合を除いて、ここではどこにも適用されません。関数が呼び出される場合は、次のサブセクションで説明するように正常に動作します。
4. 入力関数コード
関数を呼び出すときに関数コードの入力が行われます。
関数を呼び出す構文には 4 つのカテゴリがあります。
実際の関数呼び出しは電話AO はコンテキストから決定されたthisValueで呼び出されます。この引数は、呼び出し関連の呼び出しの長いチェーンで渡されます。電話呼び出し[[電話]]関数の内部スロット。これは通常通話の準備新しい機能環境レコード創造された:
関数環境レコードは、関数の最上位スコープを表すために使用される宣言型環境レコードであり、関数が ArrowFunction でない場合はバインディングを提供します
this
。関数がArrowFunction関数ではなく、 を参照する場合super
、その関数環境レコードには、関数内からメソッド呼び出しを実行するために使用される状態も含まれますsuper
。
さらに、関数環境レコードには [[ThisValue]] フィールドがあります。
this
これは、関数の呼び出しに使用される値です。
の新しい関数環境呼び出しにより、関数環境の [[ThisBindingStatus]] プロパティも設定されます。
[[電話]]また、通常呼び出しバインドここで、適切なthisArgument は以下に基づいて決定されます。
- 元の参照、
- 関数の種類、そして
- コードが厳密モード。
決定したら、最終的なBindThisValue新しく作成された関数 Environment Record のメソッドは、実際に [[ThisValue]] フィールドに thisArgument を設定します。
最後に、この分野は、機能環境レコードの GetThisBindingAO は次の値を取得しますthis
:
関数 Environment Record envRecの GetThisBinding 具象メソッドは[…] [次のことを行います]:
[…]
3. envRec .[[ThisValue]] を返します。
繰り返しになりますが、この値がどのように決定されるかは多くの要因に依存します。これは単なる概要にすぎません。この技術的な背景を踏まえて、具体的な例をすべて検証してみましょう。
矢印関数
とき矢印関数評価されると、関数オブジェクトの[[ThisMode]]内部スロットは「lexical」に設定されます。通常関数作成。
で通常呼び出しバインド関数Fを受け取ります。
- thisModeをF .[[ThisMode]]とします。
- thisModeがlexicalの場合は、NormalCompletion(
undefined
) を返します。[…]
これは、 thisをバインドするアルゴリズムの残りの部分がスキップされることを意味します。矢印関数は、自身のthis値をバインドしません。
では、this
矢印関数の中身は何なのでしょうか?このバインディングを解決するそしてこの環境を取得する、HasThisBindingメソッドは明示的にfalseを返します。
関数 Environment Record envRecの HasThisBinding 具象メソッドは[…] [次のことを行います]:
- envRec .[[ThisBindingStatus]] が字句規則に従う場合はfalse を返し、そうでない場合はtrueを返します。
そのため、代わりに外部環境が繰り返し検索されます。プロセスは、thisバインディングを持つ 3 つの環境のいずれかで終了します。
これは、矢印関数本体において、this
矢印関数の語彙スコープから来ることを意味します。言い換えれば、(矢印関数と関数宣言/式: それらは同等ですか/交換可能ですか?):
矢印関数には独自の […] バインディングはありません
this
。代わりに、[この識別子は] 他の変数と同様にレキシカル スコープ内で解決されます。つまり、矢印関数内では、矢印関数が定義されている環境(つまり、矢印関数の「外部」) のthis
[ の値 ] を参照します。this
関数プロパティ
通常の関数(function
、方法) は、関数の呼び出し方法によってthis
決まります。
ここで、「構文バリアント」が役に立ちます。
関数を含む次のオブジェクトを考えてみましょう。
const refObj = {
func: function(){
console.log(this);
}
};
あるいは:
const refObj = {
func(){
console.log(this);
}
};
以下の関数呼び出しのいずれにおいても、this
内部の値は. 1func
になります。refObj
refObj.func()
refObj["func"]()
refObj?.func()
refObj.func?.()
refObj.func``
呼び出された関数が構文的に基底オブジェクトのプロパティである場合、この基底は呼び出しの「参照」となり、通常は の値になりますthis
。これは、上にリンクされている評価手順で説明されています。たとえば、refObj.func()
(またはrefObj["func"]()
) では、呼び出しメンバー式refObj.func()
は、次の式全体です。メンバー表現 refObj.func
そしてその引数 ()
。
しかし、それぞれ次の 3 つの役割も果たしますrefObj.func
。refObj
- どちらも表現です
- どちらも参考資料であり、
- どちらも価値あるものです。
refObj.func
値は呼び出し可能な関数オブジェクトであり、対応する参照がバインディングを決定するために使用されますthis
。
オプションの連鎖とタグ付きテンプレートの例は非常によく似た動作をします。基本的に、参照は の前?.()
、 の前``
、または の前のすべてです()
。
評価通話用途プロパティ参照参照の [[Base]] プロパティを取得しようとしています (たとえばrefObj
、 に適用された場合はrefObj.func
、foo.bar
に適用された場合はfoo.bar.baz
)。 プロパティとして記述されている場合は、この値を取得この [[Base]] プロパティを取得し、それをthis値として使用します。
注記:ゲッター / セッターに関しては、メソッドと同じように動作しますthis
。単純なプロパティは実行コンテキストに影響を与えません。たとえば、ここでは、this
はグローバル スコープ内にあります。
const o = {
a: 1,
b: this.a, // Is `globalThis.a`.
[this.a]: 2 // Refers to `globalThis.a`.
};
ベース参照なしの呼び出し、厳密モード、およびwith
ベース参照のない呼び出しは、通常、プロパティとして呼び出されない関数です。例:
func(); // As opposed to `refObj.func();`.
これは次のような場合にも起こりますメソッドの受け渡しまたは割り当て、またはカンマ演算子ここで、参照レコードと値の違いが重要になります。
関数の注意: 仕様に従うと、関数オブジェクト (値) 自体のみを返すことができ、参照レコードを返すことはできないことがj
わかります。そのため、基本参照は失われます。j
refObj
const g = (f) => f(); // No base ref.
const h = refObj.func;
const j = () => refObj.func;
g(refObj.func);
h(); // No base ref.
j()(); // No base ref.
(0, refObj.func)(); // Another common pattern to remove the base ref.
評価通話通話電話ここではthisValueがundefinedです。これにより、通常呼び出しバインド( F : 関数オブジェクト; thisArgument :渡されるthisValue電話):
- thisModeをF .[[ThisMode]]とします。
[…]
- thisModeがstrictの場合、thisValue をthisArgumentにします。
- それ以外、
[…]
注:この場合、ステップ 5 では、厳密モードで this
指定されたthisArgumentに実際の値が設定されます。「sloppy モード」では、未定義または null のthisArgumentがグローバルthis値になります。undefined
this
もしプロパティ参照falseを返す場合、評価通話次の手順に従います。
- refEnvをref .[[Base]]とします。
- アサート: refEnvは環境レコードです。
- thisValue をrefEnv .WithBaseObject()にします。
未定義のthisValueが返される可能性があるのは、refEnvです。ベースオブジェクト() は常にundefinedですが、with
ステートメント。この場合、thisValue がバインディング オブジェクトになります。
また、Symbol.unscopables
(MDN のドキュメント) を使用してwith
バインディング動作を制御します。
これまでのところをまとめると、次のようになります。
function f1(){
console.log(this);
}
function f2(){
console.log(this);
}
function f3(){
console.log(this);
}
const o = {
f1,
f2,
[Symbol.unscopables]: {
f2: true
}
};
f1(); // Logs `globalThis`.
with(o){
f1(); // Logs `o`.
f2(); // `f2` is unscopable, so this logs `globalThis`.
f3(); // `f3` is not on `o`, so this logs `globalThis`.
}
そして:
"use strict";
function f(){
console.log(this);
}
f(); // Logs `undefined`.
// `with` statements are not allowed in strict-mode code.
を評価するときthis
、通常の関数がどこで定義されているかは関係ないことに注意してください。
.call
、.apply
、.bind
、thisArg、およびプリミティブ
Another consequence of step 5 of OrdinaryCallBindThis, in conjunction with step 6.2 (6.b in the spec), is that a primitive this value is coerced to an object only in “sloppy” mode.
To examine this, let’s introduce another source for the this value: the three methods that override the this binding:4
Function.prototype.apply(thisArg, argArray)
Function.prototype.
{call
,bind
}(thisArg, ...args)
.bind
creates a bound function, whose this binding is set to thisArg and cannot change again. .call
and .apply
call the function immediately, with the this binding set to thisArg.
.call
and .apply
map directly to Call, using the specified thisArg. .bind
creates a bound function with BoundFunctionCreate. These have their own [[Call]] method which looks up the function object’s [[BoundThis]] internal slot.
Examples of setting a custom this value:
function f(){
console.log(this);
}
const myObj = {},
g = f.bind(myObj),
h = (m) => m();
// All of these log `myObj`.
g();
f.bind(myObj)();
f.call(myObj);
h(g);
For objects, this is the same in strict and non-strict mode.
Now, try to supply a primitive value:
function f(){
console.log(this);
}
const myString = "s",
g = f.bind(myString);
g(); // Logs `String { "s" }`.
f.call(myString); // Logs `String { "s" }`.
In non-strict mode, primitives are coerced to their object-wrapped form. It’s the same kind of object you get when calling Object("s")
or new String("s")
. In strict mode, you can use primitives:
"use strict";
function f(){
console.log(this);
}
const myString = "s",
g = f.bind(myString);
g(); // Logs `"s"`.
f.call(myString); // Logs `"s"`.
Libraries make use of these methods, e.g. jQuery sets the this
to the DOM element selected here:
$("button").click(function(){
console.log(this); // Logs the clicked button.
});
Constructors, classes, and new
When calling a function as a constructor using the new
operator, EvaluateNew calls Construct, which calls the [[Construct]] method. If the function is a base constructor (i.e. not a class extends
…{
…}
), it sets thisArgument to a new object created from the constructor’s prototype. Properties set on this
in the constructor will end up on the resulting instance object. this
is implicitly returned, unless you explicitly return your own non-primitive value.
A class
is a new way of creating constructor functions, introduced in ECMAScript 2015.
function Old(a){
this.p = a;
}
const o = new Old(1);
console.log(o); // Logs `Old { p: 1 }`.
class New{
constructor(a){
this.p = a;
}
}
const n = new New(1);
console.log(n); // Logs `New { p: 1 }`.
Class definitions are implicitly in strict mode:
class A{
m1(){
return this;
}
m2(){
const m1 = this.m1;
console.log(m1());
}
}
new A().m2(); // Logs `undefined`.
super
The exception to the behavior with new
is class extends
…{
…}
, as mentioned above. Derived classes do not immediately set their this value upon invocation; they only do so once the base class is reached through a series of super
calls (happens implicitly without an own constructor
). Using this
before calling super
is not allowed.
Calling super
calls the super constructor with the this value of the lexical scope (the function Environment Record) of the call. GetThisValue has a special rule for super
calls. It uses BindThisValue to set this
to that Environment Record.
class DerivedNew extends New{
constructor(a, a2){
// Using `this` before `super` results in a ReferenceError.
super(a);
this.p2 = a2;
}
}
const n2 = new DerivedNew(1, 2);
console.log(n2); // Logs `DerivedNew { p: 1, p2: 2 }`.
5. Evaluating class fields
Instance fields and static fields were introduced in ECMAScript 2022.
When a class
is evaluated, ClassDefinitionEvaluation is performed, modifying the running execution context. For each ClassElement:
- if a field is static, then
this
refers to the class itself, - if a field is not static, then
this
refers to the instance.
Private fields (e.g. #x
) and methods are added to a PrivateEnvironment.
Static blocks are currently a TC39 stage 3 proposal. Static blocks work the same as static fields and methods: this
inside them refers to the class itself.
Note that in methods and getters / setters, this
works just like in normal function properties.
class Demo{
a = this;
b(){
return this;
}
static c = this;
static d(){
return this;
}
// Getters, setters, private modifiers are also possible.
}
const demo = new Demo;
console.log(demo.a, demo.b()); // Both log `demo`.
console.log(Demo.c, Demo.d()); // Both log `Demo`.
1: (o.f)()
is equivalent to o.f()
; (f)()
is equivalent to f()
. This is explained in this 2ality article (archived). Particularly see how a ParenthesizedExpression is evaluated.
2: It must be a MemberExpression, must not be a property, must have a [[ReferencedName]] of exactly "eval", and must be the %eval% intrinsic object.
3: Whenever the specification says “Let ref be the result of evaluating X.”, then X is some expression that you need to find the evaluation steps for. For example, evaluating a MemberExpression or CallExpression is the result of one of these algorithms. Some of them result in a Reference Record.
4: There are also several other native and host methods that allow providing a this value, notably Array.prototype.map
, Array.prototype.forEach
, etc. that accept a thisArg as their second argument. Anyone can make their own methods to alter this
like (func, thisArg) => func.bind(thisArg)
, (func, thisArg) => func.call(thisArg)
, etc. As always, MDN offers great documentation.
Just for fun, test your understanding with some examples
For each code snippet, answer the question: “What is the value of this
at the marked line? Why?”.
To reveal the answers, click the gray boxes.
-
if(true){ console.log(this); // What is `this` here? }
globalThis
. The marked line is evaluated in the initial global execution context. -
const obj = {}; function myFun(){ return { // What is `this` here? "is obj": this === obj, "is globalThis": this === globalThis }; } obj.method = myFun; console.log(obj.method());
obj
. When calling a function as a property of an object, it is called with the this binding set to the base of the referenceobj.method
, i.e.obj
. -
const obj = { myMethod: function(){ return { // What is `this` here? "is obj": this === obj, "is globalThis": this === globalThis }; } }, myFun = obj.myMethod; console.log(myFun());
globalThis
. Since the function valuemyFun
/obj.myMethod
is not called off of an object, as a property, the this binding will beglobalThis
. This is different from Python, in which accessing a method (obj.myMethod
) creates a bound method object. -
const obj = { myFun: () => ({ // What is `this` here? "is obj": this === obj, "is globalThis": this === globalThis }) }; console.log(obj.myFun());
globalThis
. Arrow functions don’t create their own this binding. The lexical scope is the same as the initial global scope, sothis
isglobalThis
. -
function myFun(){ console.log(this); // What is `this` here? } const obj = { myMethod: function(){ eval("myFun()"); } }; obj.myMethod();
globalThis
直接の eval 呼び出しを評価する場合、this
は ですobj
。ただし、 eval コードでは、myFun
はオブジェクトから呼び出されないため、thisバインディングはグローバル オブジェクトに設定されます。 -
function myFun() { // What is `this` here? return { "is obj": this === obj, "is globalThis": this === globalThis }; } const obj = {}; console.log(myFun.call(obj));
obj
この行は、最初の引数として受け入れるmyFun.call(obj);
特殊な組み込み関数 を呼び出しています。Function.prototype.call
thisArg
-
class MyCls{ arrow = () => ({ // What is `this` here? "is MyCls": this === MyCls, "is globalThis": this === globalThis, "is instance": this instanceof MyCls }); } console.log(new MyCls().arrow());
これは のインスタンスです。矢印関数はthis
MyCls
バインディングを変更しないため、これはレキシカル スコープから取得されます。したがって、これはのような上記のクラス フィールドとまったく同じです。これを に変更してみてください。期待どおりの結果が得られますか?a = this;
static arrow