「this」キーワードの機能とその正しい使用方法についての明確な説明を見つけたいと思っています。
奇妙な動作をしているようですが、その理由がまったくわかりません。
thisそれはどのように機能し、いつ使用する必要がありますか?
this
this キーワードは、JavaScript では他の言語と比べて動作が異なります。オブジェクト指向言語では、this キーワードはクラスの現在のインスタンスを参照します。 JavaScript では、this の値は、関数の呼び出しコンテキスト (context.function()) と関数の呼び出し元の場所によって決まります。 p>
context.function()
1.グローバルコンテキストで使用する場合
グローバル コンテキストで this を使用すると、グローバル オブジェクト (ブラウザの window) にバインドされます。 リーリー
window
this を使用する場合、その関数は実際にはグローバル コンテキストのメソッドであるため、this は依然としてグローバル オブジェクトにバインドされます。 リーリー 上記の
を使用する場合、その関数は実際にはグローバル コンテキストのメソッドであるため、
は依然としてグローバル オブジェクトにバインドされます。
f1 はグローバルオブジェクトのメソッドです。したがって、window オブジェクト上で次のように呼び出すこともできます: リーリー
はグローバルオブジェクトのメソッドです。したがって、
オブジェクト上で次のように呼び出すこともできます:
2.オブジェクトメソッド内で使用する場合
this キーワードを使用すると、this は「直接」囲んでいるオブジェクトにバインドされます。 リーリー
キーワードを使用すると、
は「直接」囲んでいるオブジェクトにバインドされます。
this は直接の親オブジェクトにバインドされることを示します。 リーリー
は直接の親オブジェクトにバインドされることを示します。
this は依然として直接の親オブジェクトを指します。 リーリー
は依然として直接の親オブジェクトを指します。
3.コンテキストフリー関数を呼び出す場合
this を使用すると、その関数はグローバル オブジェクト (ブラウザ ## の window#) にバインドされます (関数がオブジェクト内で定義されている場合でも)。 リーリー
を使用すると、その関数はグローバル オブジェクト (ブラウザ ## の
リーリー
関数を使用して上記の点を試すこともできます。しかし、まだいくつかの違いがあります。
を使用して上記で行ったことをすべて試しましたが、最初にオブジェクトを直接記述する代わりに関数を作成しました。 リーリー
内で使用される場合。 関数がコンストラクターとして使用される場合 (つまり、
キーワードを使用して呼び出される場合)、関数本体の this は、構築される新しいオブジェクトを指します。 リーリー 5.プロトタイプチェーンで定義された関数内で使用される場合
メソッドがオブジェクトのプロトタイプ チェーン上にある場合、メソッド内の
は、メソッドがそのオブジェクト上で定義されているかのように、メソッドが呼び出されるオブジェクトを参照します。 リーリー
Function.prototype
fun.apply(obj1 [, argsArray])
obj1
fun()
argsArray
fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])
fun として設定します()
arg1、arg2、arg3、...
fun() を呼び出します。
fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])
funへの参照を返します。
fun
arg1、arg2、arg3 にバインドされます。 、...
apply
call
bind
length
7. イベント ハンドラー内の this
addeventListener
onclick
attachEvent
JSFiddle中更好地尝试此操作>.
8. ES6 アロー関数の this
アロー関数では、this はパブリック変数のように動作します。つまり、その字句スコープから継承されます。アロー関数を定義する関数の this は、アロー関数の this になります。
つまり、これは次と同じ動作です:
次のコードを参照してください:
This は、実行コンテキストの属性である JavaScript のキーワードです。主に関数とコンストラクターで使用されます。 this のルールは非常に簡単です (ベスト プラクティスに従う場合)。
This
ECMAScript StandardDefinitionthis 抽象操作 (AO と省略) による ResolveThisBinding:
グローバル環境レコード、モジュール環境レコード、および関数環境レコードには、それぞれ独自の GetThisBinding メソッドがあります。
GetThisEnvironment AO は、現在の 実行コンテキストの LexicalEnvironment を検索し、 を持つ最も近い昇順の環境レコードを ([[OuterEnv]] プロパティを反復処理することにより) 検索します。 this バインディング (つまり、HasThisBinding は true を返します)。プロセスは、3 つの環境レコード タイプのいずれかで終了します。
this の値は通常、コードが strict モード であるかどうかによって異なります。
GetThisBinding の戻り値は、現在の実行コンテキストの this の値を反映するため、新しい実行コンテキストが確立されるたびに、this は異なる値に解決されます。これは、現在の実行コンテキストが変更された場合にも発生する可能性があります。次のサブセクションでは、これが発生する可能性のある 5 つのシナリオを示します。
AST Explorer にコード サンプルを配置して、仕様の詳細に従うことができます。
で直接)
this を評価すると、GetThisBinding が次の手順を実行します。 グローバル環境レコードの [[GlobalThisValue]] プロパティは、常に、
を評価すると、
globalThis ( Web ウィンドウ、Node.js の global、MDN の Documentation)。 [[GlobalThisValue]] プロパティがどのように生成されるかを学習するには、InitializeHostDefinedRealm の手順に従ってください。 ###2。 module のグローバル実行コンテキスト モジュールは ECMAScript 2015 で導入されました。
global
)。 [[GlobalThisValue]] プロパティがどのように生成されるかを学習するには、
の手順に従ってください。
内のモジュールに直接適用されます。 モジュールの初期グローバル実行コンテキスト内で this を評価すると、GetThisBinding は次の手順を実行します。 モジュール内の this の値は、グローバル コンテキストでは常に 未定義です。モジュールは暗黙的に strict モードになっています。 ###3。 evalコードを入力してください #eval 呼び出しには、direct と indirect の 2 つのタイプがあります。この区別は ECMAScript 第 5 版から存在しています。 直接の eval 呼び出しは通常、eval(...); または (eval)(...## のようになります。 #); (または ((eval))(…); など)。 1 これは direct のみです (呼び出し側の式が狭いパターンに準拠している場合)。 2 間接 eval呼び出しには、他の方法で関数参照 eval
モジュールの初期グローバル実行コンテキスト内で this を評価すると、GetThisBinding は次の手順を実行します。 モジュール内の this の値は、グローバル コンテキストでは常に 未定義です。モジュールは暗黙的に strict モードになっています。 ###3。 evalコードを入力してください #eval 呼び出しには、direct と indirect の 2 つのタイプがあります。この区別は ECMAScript 第 5 版から存在しています。 直接の eval 呼び出しは通常、eval(...); または (eval)(...## のようになります。 #); (または ((eval))(…);
モジュールの初期グローバル実行コンテキスト内で this を評価すると、GetThisBinding は次の手順を実行します。
this の値は、グローバル コンテキストでは常に 未定義です。モジュールは暗黙的に strict モードになっています。 ###3。
の値は、グローバル コンテキストでは常に
です。モジュールは暗黙的に
direct と indirect の 2 つのタイプがあります。この区別は ECMAScript 第 5 版から存在しています。 直接の
と
...
または
...## のようになります。 #);
((eval))(
1
direct
eval?.(
)
(
, eval)(
window.eval(
), eval.call(...,...) const aliasEval1 = eval; などを指定します。 window.aliasEval2 = eval;、または aliasEval1(…)、aliasEval2(…)code>.それぞれ const を与えます。 originalEval = eval; window.eval = (x) =>originalEval(x);、eval(…)
eval.call(
,
) const aliasEval1 = eval; などを指定します。 window.aliasEval2 = eval;
aliasEval1(
aliasEval2(
、
…
「JavaScript における (1, eval)('this') と eval('this') の違い」に対する
Dmitry Soshnikov の ECMA-262-5 の詳細 – 第 2 章: 間接法を使用する可能性がある状況のための厳密モード (Archived) eval() 通話状況。 PerformEval
PerformEval
コードを実行します。新しい 宣言型環境レコード をその LexicalEnvironment として作成します。GetThisEnvironment はそこから this 値を取得します。 その後、eval コードに
GetThisEnvironment
eval
が出現すると、GetThisEnvironment が呼び出され、その値が返されます。 作成される 宣言型環境レコード は、
呼び出しが直接的か間接的かによって異なります。
直接評価では、
という名前の字句スコープから取得されます。
間接 eval では、
) です。
についてはどうですか? — new Function は eval に似ていますが、コードをすぐに呼び出すのではなく、関数を作成します。 this バインディングは、関数が呼び出されるときを除き、ここではどこにも適用されません。次のサブセクションで説明するように、関数は適切に動作します。 ###4。 関数コード
バインディングは、関数が呼び出されるときを除き、ここではどこにも適用されません。次のサブセクションで説明するように、関数は適切に動作します。
次の 3 つの実行の場合
AO:
[[Call]] 関数を呼び出すための内部スロット。これにより、PrepareForOrdinaryCall が呼び出され、新しい 関数環境レコード が作成されます。 さらに、関数環境レコードには [[ThisValue]] フィールドもあります: NewFunctionEnvironment この呼び出しでは、関数環境の [[ThisBindingStatus]] プロパティも設定します。
は OrdinaryCallBindThis も呼び出します。ここで、適切な
は次の内容に基づいて決定されます。
確認後、新しく作成された関数環境レコードの BindThisValue メソッドが最終的に呼び出され、実際に [[ThisValue]] フィールドが thisArgument に追加されるように設定されます。
最後に、このフィールドは 関数環境レコード です。 GetThisBinding AO は、次の場所から this の値を取得します。
繰り返しますが、this 値の正確な決定は多くの要因に依存します。これは単なる一般的な概要です。このような技術的な背景を踏まえて、具体的な例をすべて見てみましょう。
アロー関数 を評価するとき、[[ThisMode]] 内部スロット関数オブジェクトのプロパティは、OrdinaryFunctionCreate で「lexical」に設定されます。 p>
OrdinaryCallBindThis では、関数 F:
これは単に、残りのアルゴリズム バインディング this がスキップされることを意味します。アロー関数は、独自の this 値をバインドしません。
それでは、アロー関数の this とは何でしょうか? ResolveThisBinding と GetThisEnvironment を思い出すと、HasThisBinding メソッドは明示的に false を返します。
したがって、私たちは外部環境を繰り返し探します。このプロセスは、this バインディングを持つ 3 つの環境のいずれかで終了します。
これは、アロー関数本体の this がアロー関数の語彙スコープ から、つまり ( アロー関数と. 関数宣言/式 式: これらは同等/交換可能ですか?):
通常の関数 (function、method) では、this は 関数呼び出しメソッドによって決まります。
function
ここで、これらの「構文のバリエーション」が役に立ちます。
関数を含むこのオブジェクトについて考えてみましょう:
次の関数呼び出しでは、func 内の this の値は refObj になります。 1
func
refObj
refObj.func()
refObj["関数"]()
refObj?.func()
refObj.func?.()
refObj.func``
呼び出された関数が構文的に基本オブジェクトのプロパティである場合、その基本オブジェクトは呼び出しの「参照」となり、通常の場合は this の値になります。これについては、上にリンクされている評価手順で説明しています。たとえば、refObj.func() (または refObj["func"]()) の CallMemberExpression は、式 refObj.func() 全体です。これは、MemberExpression refObj.func と # で構成されます。 ##パラメーター###### ###()###。 さらに、refObj.func と refObj
refObj["func"]()
refObj.func
これらはすべて式です、 これらはすべて参考資料であり、
参照 は this バインディングを決定するために使用されます。 オプションのリンクとタグ テンプレートの例は非常によく似ています。基本的に、参照は ?.() 前、``
は
前、
() 。 EvaluateCall 代码>IsPropertyReference
。
refObj.func に適用される場合は refObj、適用される場合は foo.bar code> foo.bar.baz)。プロパティとして記述された場合、GetThisValue はこの [[Base]] プロパティを取得し、それを this 値として使用します。 注: Getters / Setters の作業メソッドとメソッド (this
code>
)。プロパティとして記述された場合、
はこの [[Base]] プロパティを取得し、それを
値として使用します。
this のように、実行コンテキストには影響しません。 リーリー 基本参照なし、厳密モード、および with
メソッド を渡すか代入する場合、または
j についての注意: 仕様によれば、j は参照レコードではなく、関数オブジェクト (値) 自体のみを返すことができることに注意してください。したがって、ベース参照 refObj
EvaluateCall Call を呼び出します。ここで、thisValue は 未定義です。これは、 OrdinaryCallBindThis (F: 関数オブジェクト; thisArgument: thisValue が Call に渡される) では異なります。 # 注: ステップ 5 では、
this の実際の値を、厳密モードで提供される thisArgument (この場合は unknown) に設定します。 「ずさんなモード」では、未定義または null thisArgument により、this がグローバル this 値になります。
の実際の値を、厳密モードで提供される
) に設定します。 「ずさんなモード」では、未定義または null
がグローバル
が false を返した場合、EvaluateCall は次の手順を実行します。 これは未定義です thisValue
refEnv。 WithBaseObject() は、 を除き、常に unknown です。 -evaluation" rel="noreferrer">withSymbol.unscopables (Documentation on MDN) もあります。 これまでの内容を要約すると: リーリー ###そして:### リーリー
これまでの内容を要約すると:
を計算する場合、
.call、
.call
thisArg
OrdinaryCallBindThis
これを確認するために、this 値の別のソースを導入しましょう。this バインディングをオーバーライドする 3 つのメソッド: 4
Function.prototype.apply(thisArg, argArray)
thisArg に設定されており、再度変更することはできません。 .call および .apply は関数をすぐに呼び出し、this を thisArg にバインドするように設定します。 ###。 .call と .apply は、指定された thisArg を使用して、Call に直接マッピングされます。 .bind BoundFunctionCreate を使用して、バインドされた関数を作成します。これらには独自の [[Call ]] メソッド があり、関数オブジェクトの [[BoundThis]] 内部スロットを検索します。 カスタム this 値の設定例: リーリー オブジェクトの場合、これは厳密モードでも非厳密モードでも同じです。 ここで、プリミティブ値を指定してみます: リーリー 非厳密モードでは、プリミティブはオブジェクト ラッパー形式に強制されます。これは、Object("s") または new String("s") を呼び出したときに取得するオブジェクト型と同じです。厳密モードでは、 プリミティブを使用できます: リーリー これらのメソッドを利用するライブラリ (jQuery など) は、ここで選択した DOM 要素に this を設定します: リーリー コンストラクター、クラスおよびNew new 演算子を使用して関数がコンストラクターとして呼び出される場合、EvaluateNew は Construct を呼び出し、これにより [[Construct]] メソッドが呼び出されます。 ###。関数が基本コンストラクターである場合 (つまり、class extends...{...} ではない)、thisArgument を次のように設定します。コンストラクターのプロトタイプから作成された新しいオブジェクト。コンストラクターの this に設定されたプロパティは、最終的に生成されたインスタンス オブジェクトに表示されます。 this は、独自の非プリミティブ値を明示的に返さない限り、暗黙的に返されます。 class は、ECMAScript 2015 で導入された、コンストラクターを作成する新しい方法です。 リーリー クラス定義は暗黙的に strict モード: リーリー #########素晴らしい######### new 動作の例外は、上で説明した class extends...{... H4>} です。派生クラスは、呼び出されてすぐに this 値を設定しません。一連の super 呼び出しを通じて基本クラスに到達した後でのみ設定します (独自の コンストラクターは暗黙的に発生しません)。関数 の場合)。 super を呼び出す前に this を使用することはできません。 Call super 呼び出しの字句スコープ (関数環境レコード) からの this 値を使用してスーパー コンストラクターを呼び出します。 GetThisValue super 呼び出しには特別なルールがあります。 BindThisValue を使用して、this をその環境レコードに設定します。 リーリー ###5。評価クラスのフィールド インスタンス フィールドと静的フィールドは ECMAScript 2022 で導入されました。 class を評価すると、ClassDefinitionEvaluation が実行され、実行中の実行コンテキストが変更されます。各 ClassElement: フィールドが静的な場合、this はクラス自体を参照します。 フィールドが静的でない場合、this はインスタンスを参照します。 プライベート フィールド (例: #x) とメソッドがプライベート環境に追加されます。 静的ブロックは現在、TC39フェーズ3提案です。静的ブロックは静的フィールドやメソッドと同じように機能します。静的ブロック内の this はクラス自体を参照します。 メソッドおよびゲッター/セッターでは、this は通常の関数プロパティと同じように機能することに注意してください。 リーリー 1: (o.f)() は o.f() と同等; (f)() は # と同等# #f()。 この 2ality 記事 (アーカイブ済み )。特に括弧付き式を評価する方法を参照してください。 2: MemberExpression である必要があります。プロパティにすることはできません。[[] の正確な "eval" を持つ必要があります。 ReferencedName]] であり、%eval% 組み込みオブジェクトである必要があります。 em> 3: 仕様に「 ref を式の評価結果とする。」と記載されている場合は常に、たとえば、MemberExpression または CallExpression の評価は、これらのアルゴリズムです。これらの一部は 参照レコード を生成します。 4: this 値を指定できるネイティブ メソッドとホスト メソッドが他にもいくつかあります (特に Array.prototype.map、Array .prototype)。 .forEach などは thisArg を 2 番目の引数として受け入れます。誰もが this を変更する独自のメソッドを作成できます (例: (func, thisArg) => func.bind(thisArg), (func, thisArg) => func 。 call(thisArg) など。いつものように、MDN は優れたサービス ドキュメントを提供します。 お楽しみのために、いくつかの例で理解をテストしてください 各コード スニペットについて、次の質問に答えてください: 「マークされた行の this の値は何ですか?なぜですか?」 . 答えを表示するには、灰色のボックスをクリックします。 リーリー グローバルこれ。マークされた行は、初期のグローバル実行コンテキスト内で評価されます。 リーリー
.call と .apply は、指定された thisArg を使用して、Call に直接マッピングされます。 .bind BoundFunctionCreate を使用して、バインドされた関数を作成します。これらには独自の [[Call ]] メソッド があり、関数オブジェクトの [[BoundThis]] 内部スロットを検索します。
.apply
.bind
カスタム this 値の設定例:
オブジェクトの場合、これは厳密モードでも非厳密モードでも同じです。
ここで、プリミティブ値を指定してみます:
非厳密モードでは、プリミティブはオブジェクト ラッパー形式に強制されます。これは、Object("s") または new String("s") を呼び出したときに取得するオブジェクト型と同じです。厳密モードでは、 プリミティブを使用できます: リーリー
Object("s")
new String("s")
this を設定します: リーリー
を設定します:
new 演算子を使用して関数がコンストラクターとして呼び出される場合、EvaluateNew は Construct を呼び出し、これにより [[Construct]] メソッドが呼び出されます。 ###。関数が基本コンストラクターである場合 (つまり、class extends...{...} ではない)、thisArgument を次のように設定します。コンストラクターのプロトタイプから作成された新しいオブジェクト。コンストラクターの this に設定されたプロパティは、最終的に生成されたインスタンス オブジェクトに表示されます。 this は、独自の非プリミティブ値を明示的に返さない限り、暗黙的に返されます。
演算子を使用して関数がコンストラクターとして呼び出される場合、
{
}
thisArgument
class は、ECMAScript 2015 で導入された、コンストラクターを作成する新しい方法です。 リーリー クラス定義は暗黙的に
は、ECMAScript 2015 で導入された、コンストラクターを作成する新しい方法です。
: リーリー #########素晴らしい#########
です。派生クラスは、呼び出されてすぐに this 値を設定しません。一連の super 呼び出しを通じて基本クラスに到達した後でのみ設定します (独自の コンストラクターは暗黙的に発生しません)。関数 の場合)。 super を呼び出す前に this を使用することはできません。 Call super 呼び出しの字句スコープ (関数環境レコード) からの this 値を使用してスーパー コンストラクターを呼び出します。
super
コンストラクターは暗黙的に発生しません)。関数
super 呼び出しには特別なルールがあります。 BindThisValue を使用して、this をその環境レコードに設定します。 リーリー ###5。評価クラスのフィールド インスタンス フィールドと静的フィールドは ECMAScript 2022 で導入されました。 class を評価すると、ClassDefinitionEvaluation が実行され、実行中の実行コンテキストが変更されます。各 ClassElement: フィールドが静的な場合、this はクラス自体を参照します。 フィールドが静的でない場合、this はインスタンスを参照します。 プライベート フィールド (例: #x) とメソッドがプライベート環境に追加されます。 静的ブロックは現在、TC39フェーズ3提案です。静的ブロックは静的フィールドやメソッドと同じように機能します。静的ブロック内の this はクラス自体を参照します。 メソッドおよびゲッター/セッターでは、this は通常の関数プロパティと同じように機能することに注意してください。 リーリー 1: (o.f)() は o.f() と同等; (f)() は # と同等# #f()。 この 2ality 記事 (アーカイブ済み )。特に括弧付き式を評価する方法を参照してください。 2: MemberExpression である必要があります。プロパティにすることはできません。[[] の正確な "eval" を持つ必要があります。 ReferencedName]] であり、%eval% 組み込みオブジェクトである必要があります。 em> 3: 仕様に「 ref を式の評価結果とする。」と記載されている場合は常に、たとえば、MemberExpression または CallExpression の評価は、これらのアルゴリズムです。これらの一部は 参照レコード を生成します。 4: this 値を指定できるネイティブ メソッドとホスト メソッドが他にもいくつかあります (特に Array.prototype.map、Array .prototype)。 .forEach などは thisArg を 2 番目の引数として受け入れます。誰もが this を変更する独自のメソッドを作成できます (例: (func, thisArg) => func.bind(thisArg), (func, thisArg) => func 。 call(thisArg) など。いつものように、MDN は優れたサービス ドキュメントを提供します。 お楽しみのために、いくつかの例で理解をテストしてください 各コード スニペットについて、次の質問に答えてください: 「マークされた行の this の値は何ですか?なぜですか?」 . 答えを表示するには、灰色のボックスをクリックします。 リーリー グローバルこれ。マークされた行は、初期のグローバル実行コンテキスト内で評価されます。 リーリー
呼び出しには特別なルールがあります。
を評価すると、ClassDefinitionEvaluation が実行され、実行中の実行コンテキストが変更されます。各 ClassElement:
ClassDefinitionEvaluation
) とメソッドがプライベート環境に追加されます。
は現在、TC39フェーズ3提案です。静的ブロックは静的フィールドやメソッドと同じように機能します。静的ブロック内の this はクラス自体を参照します。 メソッドおよびゲッター/セッターでは、
は通常の関数プロパティと同じように機能することに注意してください。 リーリー
: (o.f)() は o.f() と同等; (f)() は # と同等# #f()。 この 2ality 記事 (アーカイブ済み )。特に括弧付き式を評価する方法を参照してください。 2
o.f()
(f)()
MemberExpression である必要があります。プロパティにすることはできません。[[] の正確な "eval" を持つ必要があります。 ReferencedName]] であり、%eval% 組み込みオブジェクトである必要があります。 em> 3
ref を式の評価結果とする。」と記載されている場合は常に、たとえば、MemberExpression または CallExpression の評価は、これらのアルゴリズムです。これらの一部は 参照レコード を生成します。 4: this 値を指定できるネイティブ メソッドとホスト メソッドが他にもいくつかあります (特に
、Array .prototype)。 .forEach などは thisArg を 2 番目の引数として受け入れます。誰もが this を変更する独自のメソッドを作成できます (例: (func, thisArg) => func.bind(thisArg), (func, thisArg) => func 。 call(thisArg) など。いつものように、MDN は優れたサービス ドキュメントを提供します。 お楽しみのために、いくつかの例で理解をテストしてください 各コード スニペットについて、次の質問に答えてください: 「マークされた行の
MDN
お楽しみのために、いくつかの例で理解をテストしてください
答えを表示するには、灰色のボックスをクリックします。 リーリー グローバルこれ。マークされた行は、初期のグローバル実行コンテキスト内で評価されます。 リーリー
グローバルこれ。マークされた行は、初期のグローバル実行コンテキスト内で評価されます。
グローバルこれ
this
キーワードは、JavaScript では他の言語と比べて動作が異なります。オブジェクト指向言語では、this
キーワードはクラスの現在のインスタンスを参照します。 JavaScript では、this
の値は、関数の呼び出しコンテキスト (context.function()
) と関数の呼び出し元の場所によって決まります。 p>1.グローバルコンテキストで使用する場合
グローバル コンテキストで
グローバル コンテキストで定義された関数内でthis
を使用すると、グローバル オブジェクト (ブラウザのwindow
) にバインドされます。 リーリーthis
を使用する場合、その関数は実際にはグローバル コンテキストのメソッドであるため、
thisは依然としてグローバル オブジェクトにバインドされます。
リーリー 上記のf1
はグローバルオブジェクトのメソッドです。したがって、
windowオブジェクト上で次のように呼び出すこともできます:
リーリー2.オブジェクトメソッド内で使用する場合
オブジェクト メソッドでthis
上では、「直ちに」という単語を二重引用符で囲みました。これは、オブジェクトが別のオブジェクト内にネストされている場合、キーワードを使用すると、
thisは「直接」囲んでいるオブジェクトにバインドされます。
リーリーthis
function をメソッドとしてオブジェクトに明示的に追加した場合でも、上記の規則に従います。つまり、は直接の親オブジェクトにバインドされることを示します。
リーリーthis
は依然として直接の親オブジェクトを指します。
リーリー3.コンテキストフリー関数を呼び出す場合
コンテキストなしで (つまり、オブジェクト上ではなく) 呼び出される関数内でthis
関数を使ってすべてを試してみるを使用すると、その関数はグローバル オブジェクト (ブラウザ ## の
window#) にバインドされます (関数がオブジェクト内で定義されている場合でも)。リーリー
関数を使用して上記の点を試すこともできます。しかし、まだいくつかの違いがあります。
this- を使用して関数にメンバーを追加できます。それらを指定します。
オブジェクト リテラル表記は、すぐに使用できるオブジェクト インスタンスを作成します。関数の場合、最初に new- 演算子を使用して関数のインスタンスを作成する必要がある場合があります。
オブジェクト リテラル メソッドでも、ドット演算子を使用して、定義されたオブジェクトにメンバーを明示的に追加できます。これは特定のインスタンスにのみ追加されます。ただし、関数のすべてのインスタンスに反映されるように、変数を関数プロトタイプに追加しました。
-
以下では、Object と
thisを使用して上記で行ったことをすべて試しましたが、最初にオブジェクトを直接記述する代わりに関数を作成しました。
4.コンストラクターリーリー
内で使用される場合。 関数がコンストラクターとして使用される場合 (つまり、
newキーワードを使用して呼び出される場合)、関数本体の
this
は、構築される新しいオブジェクトを指します。リーリー
5.プロトタイプチェーンで定義された関数内で使用される場合メソッドがオブジェクトのプロトタイプ チェーン上にある場合、メソッド内の
thisは、メソッドがそのオブジェクト上で定義されているかのように、メソッドが呼び出されるオブジェクトを参照します。
6.内部 call()、apply()、bind() 関数リーリー
Function.prototype
で定義されています。this
値を指定できます。また、元の関数が呼び出されたときに、任意の引数を渡すこともできます。fun.apply(obj1 [, argsArray])
obj1
をthis
code>fun() 内の値に設定します そしてfun()
を呼び出し、argsArray
の要素を引数として渡します。fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])
- obj1 をfun として設定します()
this
の値を取得し、arg1、arg2、arg3、...
を引数として渡してfun() を呼び出します。
fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])
- 関数funへの参照を返します。
、fun のthis
はobj1
にバインドされ、fun
のパラメータは指定されたパラメータarg1、arg2、arg3 にバインドされます。 、...
。apply
、call
、bind
の違いは明らかになるはずです。apply
引数を配列のようなオブジェクト、つまり数値length
プロパティと対応する非負の整数プロパティを持つオブジェクトとして指定できるようにします。また、call
を使用すると、関数のパラメーターを直接指定できます。apply
とcall
は両方とも、指定されたコンテキストで指定されたパラメーターを使用して関数を直ちに呼び出します。一方、bind は、指定された this 値と引数にバインドされた関数を返すだけです。この返された関数への参照を変数に割り当てることで取得でき、いつでも呼び出すことができます。7. イベント ハンドラー内の
this
this
を直接使用して、対応する要素を参照します。この直接的な関数の割り当ては、addeventListener
メソッドを使用するか、onclick
などの従来のイベント登録メソッドを介して実行できます。など) 内で
this
を直接使用すると、その要素が参照されます。this
を間接的に使用すると、グローバル オブジェクトwindow
に解決されます。attachEvent
を使用して関数をイベント ハンドラーにアタッチすると、上記と同じ動作を実現できます。イベント ハンドラーに関数を割り当てる (つまり、要素の関数メソッドを作成する) 代わりに、イベントで関数を呼び出します (事実上、グローバル コンテキストで関数を呼び出します)。JSFiddle中更好地尝试此操作>.
を使用することをお勧めします。 リーリー8. ES6 アロー関数の
this
アロー関数では、
this
はパブリック変数のように動作します。つまり、その字句スコープから継承されます。アロー関数を定義する関数のthis
は、アロー関数のthis
になります。つまり、これは次と同じ動作です:
リーリー次のコードを参照してください:
リーリーThis
は、実行コンテキストの属性である JavaScript のキーワードです。主に関数とコンストラクターで使用されます。this
のルールは非常に簡単です (ベスト プラクティスに従う場合)。仕様書における
this
の技術的説明ECMAScript StandardDefinition
this
抽象操作 (AO と省略) による ResolveThisBinding:グローバル環境レコード、モジュール環境レコード、および関数環境レコードには、それぞれ独自の GetThisBinding メソッドがあります。
GetThisEnvironment AO は、現在の 実行コンテキストの LexicalEnvironment を検索し、 を持つ最も近い昇順の環境レコードを ([[OuterEnv]] プロパティを反復処理することにより) 検索します。 this バインディング (つまり、HasThisBinding は true を返します)。プロセスは、3 つの環境レコード タイプのいずれかで終了します。
this
の値は通常、コードが strict モード であるかどうかによって異なります。GetThisBinding の戻り値は、現在の実行コンテキストの
this
の値を反映するため、新しい実行コンテキストが確立されるたびに、this
は異なる値に解決されます。これは、現在の実行コンテキストが変更された場合にも発生する可能性があります。次のサブセクションでは、これが発生する可能性のある 5 つのシナリオを示します。AST Explorer にコード サンプルを配置して、仕様の詳細に従うことができます。
###1。スクリプト内のグローバル実行コンテキストthis
を評価すると、
GetThisBinding が次の手順を実行します。 グローバル環境レコードの [[GlobalThisValue]] プロパティは、常に、globalThis ( Web ウィンドウ、Node.js の
global
、MDN の Documentation)。 [[GlobalThisValue]] プロパティがどのように生成されるかを学習するには、
InitializeHostDefinedRealmの手順に従ってください。
###2。 module のグローバル実行コンテキスト モジュールは ECMAScript 2015 で導入されました。これは、単に
ではなく、例えば内のモジュールに直接適用されます。
など)。モジュールの初期グローバル実行コンテキスト内で
モジュール内のthis
を評価すると、GetThisBinding は次の手順を実行します。this
evalの値は、グローバル コンテキストでは常に
未定義です。モジュールは暗黙的に
strict モードになっています。 ###3。コードを入力してください #eval
呼び出しには、direct
evalと
indirect の 2 つのタイプがあります。この区別は ECMAScript 第 5 版から存在しています。 直接の呼び出しは通常、- eval(
...
);または
(eval)(...## のようになります。 #);
(または((eval))(
…);1
これはdirect
のみです (呼び出し側の式が狭いパターンに準拠している場合)。 2 間接 eval呼び出しには、他の方法で関数参照 evaleval?.(
...)
,(
..., eval)(
...)
,window.eval(
...),
の呼び出しも間接的です。eval.call(
...,
...) const aliasEval1 = eval; などを指定します。 window.aliasEval2 = eval;
、またはaliasEval1(
…)
、aliasEval2(
…)
code>.それぞれ const を与えます。 originalEval = eval; window.eval = (x) =>originalEval(x);、
eval(…
)「JavaScript における (1, eval)('this') と eval('this') の違い」に対する
chuckj の回答を参照してください。Dmitry Soshnikov の ECMA-262-5 の詳細 – 第 2 章: 間接法を使用する可能性がある状況のための厳密モード (Archived) eval() 通話状況。
evalPerformEval
コードを実行します。新しい 宣言型環境レコード をその LexicalEnvironment として作成します。
thisGetThisEnvironment
はそこから this 値を取得します。 その後、eval
コードにが出現すると、
作成される 宣言型環境レコード は、
evalGetThisEnvironment
が呼び出され、その値が返されます。呼び出しが直接的か間接的かによって異なります。
直接評価では、
this値は変更されず、- eval
値はグローバル オブジェクト (- globalThis
新機能という名前の字句スコープから取得されます。
間接 eval では、
this) です。
についてはどうですか? —
を入力してくださいnew Function は
eval
に似ていますが、コードをすぐに呼び出すのではなく、関数を作成します。 thisバインディングは、関数が呼び出されるときを除き、ここではどこにも適用されません。次のサブセクションで説明するように、関数は適切に動作します。
###4。 関数コード関数を呼び出すときに関数コードを入力します。
関数を呼び出すための構文には 4 つのカテゴリがあります。次の 3 つの実行の場合
EvaluateCallAO:
3[[Call]] 関数を呼び出すための内部スロット。これにより、PrepareForOrdinaryCall が呼び出され、新しい 関数環境レコード が作成されます。 さらに、関数環境レコードには [[ThisValue]] フィールドもあります: NewFunctionEnvironment この呼び出しでは、関数環境の [[ThisBindingStatus]] プロパティも設定します。
[[Call]]は OrdinaryCallBindThis も呼び出します。ここで、適切な
thisArgumentは次の内容に基づいて決定されます。
確認後、新しく作成された関数環境レコードの BindThisValue メソッドが最終的に呼び出され、実際に [[ThisValue]] フィールドが thisArgument に追加されるように設定されます。
最後に、このフィールドは 関数環境レコード です。 GetThisBinding AO は、次の場所から
this
の値を取得します。繰り返しますが、this 値の正確な決定は多くの要因に依存します。これは単なる一般的な概要です。このような技術的な背景を踏まえて、具体的な例をすべて見てみましょう。
アロー関数
アロー関数 を評価するとき、[[ThisMode]] 内部スロット関数オブジェクトのプロパティは、OrdinaryFunctionCreate で「lexical」に設定されます。 p>
OrdinaryCallBindThis では、関数 F:
を受け取ります。これは単に、残りのアルゴリズム バインディング this がスキップされることを意味します。アロー関数は、独自の this 値をバインドしません。
それでは、アロー関数の
this
とは何でしょうか? ResolveThisBinding と GetThisEnvironment を思い出すと、HasThisBinding メソッドは明示的に false を返します。したがって、私たちは外部環境を繰り返し探します。このプロセスは、this バインディングを持つ 3 つの環境のいずれかで終了します。
これは、アロー関数本体の
this
がアロー関数の語彙スコープ から、つまり ( アロー関数と. 関数宣言/式 式: これらは同等/交換可能ですか?):関数プロパティ
通常の関数 (
function
、method) では、this
は 関数呼び出しメソッドによって決まります。ここで、これらの「構文のバリエーション」が役に立ちます。
関数を含むこのオブジェクトについて考えてみましょう:
リーリー ###または:###リーリー次の関数呼び出しでは、
func
内のthis
の値はrefObj
になります。 1refObj.func()
refObj["関数"]()
refObj?.func()
refObj.func?.()
refObj.func``
呼び出された関数が構文的に基本オブジェクトのプロパティである場合、その基本オブジェクトは呼び出しの「参照」となり、通常の場合は
はそれぞれ 3 つの役割を果たします:this
の値になります。これについては、上にリンクされている評価手順で説明しています。たとえば、refObj.func()
(またはrefObj["func"]()
) の CallMemberExpression は、式refObj.func()
全体です。これは、MemberExpressionrefObj.func
と # で構成されます。 ##パラメーター###### ###()###。 さらに、refObj.funcと
refObjそれらはすべて値です。-
- refObj.func
-
値
は呼び出し可能な関数オブジェクトであり、対応する参照
前、またはは
this バインディングを決定するために使用されます。 オプションのリンクとタグ テンプレートの例は非常によく似ています。基本的に、参照は ?.()前、
``()
を使用して、それが構文的にオブジェクトのプロパティであるかどうかを判断します。参照の [[Base]] プロパティを取得しようとします (例:。
EvaluateCall 代码>IsPropertyReference
refObj.func に適用される場合は refObj、適用される場合は foo.bar
に関する)。単純なプロパティは、グローバル スコープのcode>
foo.bar.baz)。プロパティとして記述された場合、
GetThisValueはこの [[Base]] プロパティを取得し、それを
this値として使用します。
注: Getters / Setters の作業メソッドとメソッド (thisthis のように、実行コンテキストには影響しません。 リーリー 基本参照なし、厳密モード、および
with
基本参照のない呼び出しは、通常、属性として呼び出されない関数です。例えば:### リーリー これは、
を使用する場合にも発生します。ここで、参照レコードと値の違いが関係します。メソッド を渡すか代入する場合、または
コンマ演算子j についての注意: 仕様によれば、j は参照レコードではなく、関数オブジェクト (値) 自体のみを返すことができることに注意してください。したがって、ベース参照 refObj
が失われます。リーリー
EvaluateCall Call を呼び出します。ここで、thisValue は 未定義です。これは、 OrdinaryCallBindThis (F: 関数オブジェクト; thisArgument: thisValue が Call に渡される) では異なります。 # 注: ステップ 5 では、
this
IsPropertyReferenceの実際の値を、厳密モードで提供される
thisArgument (この場合は unknown) に設定します。 「ずさんなモード」では、未定義または null
thisArgument により、thisがグローバル
this 値になります。が false を返した場合、EvaluateCall は次の手順を実行します。 これは未定義です thisValue
から取得される可能性があります:refEnv。 WithBaseObject() は、 を除き、常に unknown です。 -evaluation" rel="noreferrer">withSymbol.unscopables
this(Documentation on MDN) もあります。
これまでの内容を要約すると:
リーリー ###そして:### リーリーを計算する場合、
通常の関数が定義されている位置は問題ではないことに注意してください。
.apply.call
、、
のみであるということです。オブジェクトにキャストします。.bind、
thisArg
とプリミティブOrdinaryCallBindThis
ステップ 5 のもう 1 つの結果は、(仕様の) ステップ 6.2 とは異なり、「sloppy」モード の元の this 値はこれを確認するために、this 値の別のソースを導入しましょう。this バインディングをオーバーライドする 3 つのメソッド: 4
Function.prototype.apply(thisArg, argArray)
Function.prototype.{- Call
} - (thisArg, ...args)
バインディングはすでに,
Bind.bind
バインド関数を作成します。その 李>this
thisArg に設定されており、再度変更することはできません。
.call および .apply は関数をすぐに呼び出し、
this
を thisArg にバインドするように設定します。 ###。.call
と.apply
は、指定された thisArg を使用して、Call に直接マッピングされます。.bind
BoundFunctionCreate を使用して、バインドされた関数を作成します。これらには独自の [[Call ]] メソッド があり、関数オブジェクトの [[BoundThis]] 内部スロットを検索します。カスタム this 値の設定例:
リーリーオブジェクトの場合、これは厳密モードでも非厳密モードでも同じです。
ここで、プリミティブ値を指定してみます:
リーリー非厳密モードでは、プリミティブはオブジェクト ラッパー形式に強制されます。これは、
これらのメソッドを利用するライブラリ (jQuery など) は、ここで選択した DOM 要素にObject("s")
またはnew String("s")
を呼び出したときに取得するオブジェクト型と同じです。厳密モードでは、 プリミティブを使用できます: リーリーthis
コンストラクター、を設定します:
リーリークラスおよびNew
new
演算子を使用して関数がコンストラクターとして呼び出される場合、
EvaluateNew は Construct を呼び出し、これにより [[Construct]] メソッドが呼び出されます。 ###。関数が基本コンストラクターである場合 (つまり、class extends...{
...}
ではない)、thisArgument
を次のように設定します。コンストラクターのプロトタイプから作成された新しいオブジェクト。コンストラクターの this に設定されたプロパティは、最終的に生成されたインスタンス オブジェクトに表示されます。this
は、独自の非プリミティブ値を明示的に返さない限り、暗黙的に返されます。class
strict モードは、ECMAScript 2015 で導入された、コンストラクターを作成する新しい方法です。
リーリー クラス定義は暗黙的に: リーリー #########素晴らしい#########
new動作の例外は、上で説明した class extends
...
{... H4>}です。派生クラスは、呼び出されてすぐに
Call
GetThisValuethis
値を設定しません。一連のsuper
呼び出しを通じて基本クラスに到達した後でのみ設定します (独自のコンストラクターは暗黙的に発生しません)。関数
の場合)。super
を呼び出す前に this を使用することはできません。super
呼び出しの字句スコープ (関数環境レコード) からの
this
値を使用してスーパー コンストラクターを呼び出します。super
インスタンス フィールドと静的フィールドは ECMAScript 2022 で導入されました。
class
呼び出しには特別なルールがあります。
BindThisValue を使用して、this をその環境レコードに設定します。 リーリー ###5。評価クラスのフィールドを評価すると、
フィールドが静的な場合、ClassDefinitionEvaluation
が実行され、実行中の実行コンテキストが変更されます。各 ClassElement:this- はクラス自体を参照します。
フィールドが静的でない場合、 this- はインスタンスを参照します。
プライベート フィールド (例:
#x) とメソッドがプライベート環境に追加されます。
静的ブロックは現在、TC39フェーズ3提案です。静的ブロックは静的フィールドやメソッドと同じように機能します。静的ブロック内の this はクラス自体を参照します。
メソッドおよびゲッター/セッターでは、
thisは通常の関数プロパティと同じように機能することに注意してください。
リーリー
1
: (o.f)() は
:o.f()
と同等;(f)()
は # と同等# #f()。
この 2ality 記事(
アーカイブ済み )。特に括弧付き式を評価する方法を参照してください。 2MemberExpression である必要があります。プロパティにすることはできません。[[] の正確な "eval" を持つ必要があります。 ReferencedName]] であり、%eval% 組み込みオブジェクトである必要があります。 em> 3
: 仕様に「ref を式の評価結果とする。」と記載されている場合は常に、たとえば、MemberExpression または CallExpression の評価は、これらのアルゴリズムです。これらの一部は 参照レコード を生成します。 4: this 値を指定できるネイティブ メソッドとホスト メソッドが他にもいくつかあります (特に
Array.prototype.map、Array .prototype)。 .forEach などは thisArg を 2 番目の引数として受け入れます。誰もが
thisthis
を変更する独自のメソッドを作成できます (例: (func, thisArg) => func.bind(thisArg), (func, thisArg) => func 。 call(thisArg) など。いつものように、MDN
は優れたサービス ドキュメントを提供します。お楽しみのために、いくつかの例で理解をテストしてください
各コード スニペットについて、次の質問に答えてください: 「マークされた行のの値は何ですか?なぜですか?」
.
答えを表示するには、灰色のボックスをクリックします。
-
リーリー
-
リーリー