ホームページ  >  に質問  >  本文

「this」キーワードの機能と適切な使用法を理解する

「this」キーワードの機能とその正しい使用方法についての明確な説明を見つけたいと思っています。

奇妙な動作をしているようですが、その理由がまったくわかりません。

thisそれはどのように機能し、いつ使用する必要がありますか?

P粉155551728P粉155551728373日前539

全員に返信(2)返信します

  • P粉087951442

    P粉0879514422023-10-13 14:03:41

    this キーワードは、JavaScript では他の言語と比べて動作が異なります。オブジェクト指向言語では、this キーワードはクラスの現在のインスタンスを参照します。 JavaScript では、this の値は、関数の呼び出しコンテキスト (context.function()) と関数の呼び出し元の場所によって決まります。

    <强>1.グローバルコンテキストで使用する場合

    グローバル コンテキストで this を使用すると、グローバル オブジェクト (ブラウザの window) にバインドされます。 リーリー

    グローバル コンテキストで定義された関数内で

    this を使用する場合、その関数は実際にはグローバル コンテキストのメソッドであるため、this は依然としてグローバル オブジェクトにバインドされます。 リーリー < /p>上記の

    f1 はグローバルオブジェクトのメソッドです。したがって、window オブジェクト上で次のように呼び出すこともできます: リーリー

    2.オブジェクトメソッド内で使用する場合<强>

    オブジェクト メソッドで

    this キーワードを使用すると、this は「直接」囲んでいるオブジェクトにバインドされます。 リーリー

    上では、「直ちに」という単語を二重引用符で囲みました。これは、オブジェクトが別のオブジェクト内にネストされている場合、

    this は直接の親オブジェクトにバインドされることを示します。 リーリー

    function をメソッドとしてオブジェクトに明示的に追加した場合でも、上記の規則に従います。つまり、

    this は依然として直接の親オブジェクトを指します。 リーリー

    3.コンテキストフリー関数を呼び出す場合<强>

    コンテキストなしで (つまり、オブジェクト上ではなく) 呼び出される関数内で

    this を使用すると、その関数はグローバル オブジェクト (ブラウザ ## の window#) にバインドされます (関数がオブジェクト内で定義されている場合でも)。 リーリー

    関数を使ってすべてを試してみる

    関数を使用して上記の点を試すこともできます。しかし、まだいくつかの違いがあります。

    上記では、オブジェクト リテラル表記を使用してオブジェクトにメンバーを追加しました。
      this
    • を使用して関数にメンバーを追加できます。それらを指定します。 オブジェクト リテラル表記は、すぐに使用できるオブジェクト インスタンスを作成します。関数の場合、最初に
    • new
    • 演算子を使用して関数のインスタンスを作成する必要がある場合があります。 オブジェクト リテラル メソッドでも、ドット演算子を使用して、定義されたオブジェクトにメンバーを明示的に追加できます。これは特定のインスタンスにのみ追加されます。ただし、関数のすべてのインスタンスに反映されるように、変数を関数プロトタイプに追加しました。
    • 以下では、Object と
    this

    を使用して上記で行ったことをすべて試しましたが、最初にオブジェクトを直接記述する代わりに関数を作成しました。 リーリー

    4.コンストラクター

    内で使用される場合。 <强> 関数がコンストラクターとして使用される場合 (つまり、

    new

    キーワードを使用して呼び出される場合)、関数本体の this は、構築される新しいオブジェクトを指します。 リーリー < /p>5.プロトタイプチェーンで定義された関数内で使用される場合

    <强> メソッドがオブジェクトのプロトタイプ チェーン上にある場合、メソッド内の

    this

    は、メソッドがそのオブジェクト上で定義されているかのように、メソッドが呼び出されるオブジェクトを参照します。 リーリー

    6.内部 call()、apply()、bind() 関数

    <强>

    • これらのメソッドはすべて Function.prototype で定義されています。
    • これらのメソッドを使用すると、関数を一度記述して、それを別のコンテキストで呼び出すことができます。つまり、関数の実行時に使用される this 値を指定できます。また、元の関数が呼び出されたときに、任意の引数を渡すこともできます。
    • fun.apply(obj1 [, argsArray]) obj1this code>fun() 内の値に設定します そして fun() を呼び出し、argsArray の要素を引数として渡します。
    • fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]]) - obj1 を fun として設定します() this の値を取得し、arg1、arg2、arg3、... を引数として渡して fun()< /code> を呼び出します。
    • fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]]) - 関数funへの参照を返します。 、fun の thisobj1 にバインドされ、fun のパラメータは指定されたパラメータ arg1、arg2、arg3 にバインドされます。 、...
    • これで、applycallbind の違いは明らかになるはずです。 apply 引数を配列のようなオブジェクト、つまり数値 length プロパティと対応する非負の整数プロパティを持つオブジェクトとして指定できるようにします。また、call を使用すると、関数のパラメーターを直接指定できます。 applycall は両方とも、指定されたコンテキストで指定されたパラメーターを使用して関数を直ちに呼び出します。一方、bind は、指定された this 値と引数にバインドされた関数を返すだけです。この返された関数への参照を変数に割り当てることで取得でき、いつでも呼び出すことができます。
    リーリー

    7. イベント ハンドラー内の this

    • 関数を要素のイベント ハンドラーに直接割り当てる場合は、イベント ハンドラー関数内で this を直接使用して、対応する要素を参照します。この直接的な関数の割り当ては、addeventListener メソッドを使用するか、onclick などの従来のイベント登録メソッドを介して実行できます。
    • 同様に、要素のイベント属性 (
    • ただし、イベント ハンドラーまたはイベント プロパティ内で呼び出される他の関数を介して this を間接的に使用すると、グローバル オブジェクト window に解決されます。
    • Microsoft のイベント登録モデル メソッド attachEvent を使用して関数をイベント ハンドラーにアタッチすると、上記と同じ動作を実現できます。イベント ハンドラーに関数を割り当てる (つまり、要素の関数メソッドを作成する) 代わりに、イベントで関数を呼び出します (事実上、グローバル コンテキストで関数を呼び出します)。

    JSFiddle.

    を使用することをお勧めします。 リーリー

    <强>8. ES6 アロー関数の this

    アロー関数では、this はパブリック変数のように動作します。つまり、その字句スコープから継承されます。アロー関数を定義する関数の this は、アロー関数の this になります。

    つまり、これは次と同じ動作です:

    リーリー

    次のコードを参照してください:

    リーリー

    返事
    0
  • P粉156532706

    P粉1565327062023-10-13 11:53:32

    This は、実行コンテキストの属性である JavaScript のキーワードです。主に関数とコンストラクターで使用されます。 this のルールは非常に簡単です (ベスト プラクティスに従う場合)。

    仕様書における this の技術的説明

    ECMAScript StandardDefinitionthis 抽象操作 (AO と省略) による ResolveThisBinding:

    グローバル環境レコードモジュール環境レコード、および関数環境レコードには、それぞれ独自の GetThisBinding メソッドがあります。

    GetThisEnvironment AO は、現在の 実行コンテキストの LexicalEnvironment を検索し、 を持つ最も近い昇順の環境レコードを ([[OuterEnv]] プロパティを反復処理することにより) 検索します。 this< /em> バインディング (つまり、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 の値は、グローバル コンテキストでは常に 未定義です。モジュールは暗黙的に strict モードになっています。 ###3。

    eval

    コードを入力してください #eval

    呼び出しには、

    directindirect の 2 つのタイプがあります。この区別は ECMAScript 第 5 版から存在しています。 直接の

    eval
      呼び出しは通常、
    • eval(...); または (eval)(...## のようになります。 #); (または ((eval))(…< code>); など)。 1 これは direct のみです (呼び出し側の式が狭いパターンに準拠している場合)。 2 間接 eval呼び出しには、他の方法で関数参照
    • eval
    • を呼び出すことが含まれます。 eval?.(...), (..., eval)(... ), window.eval(...)< /code>, 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() 通話状況。 PerformEval

    eval

    コードを実行します。新しい 宣言型環境レコード をその LexicalEnvironment として作成します。GetThisEnvironment はそこから this 値を取得します。 その後、eval コードに

    this

    が出現すると、GetThisEnvironment が呼び出され、その値が返されます。 作成される 宣言型環境レコード は、

    eval

    呼び出しが直接的か間接的かによって異なります。

    直接評価では、

    this
      値は変更されず、
    • eval という名前の字句スコープから取得されます。 間接 eval では、this
    • 値はグローバル オブジェクト (
    • globalThis) です。
    新機能

    についてはどうですか? new Functioneval に似ていますが、コードをすぐに呼び出すのではなく、関数を作成します。 this バインディングは、関数が呼び出されるときを除き、ここではどこにも適用されません。次のサブセクションで説明するように、関数は適切に動作します。 ###4。 関数コード

    を入力してください

    関数を呼び出すときに関数コードを入力します。

    関数を呼び出すための構文には 4 つのカテゴリがあります。

    次の 3 つの実行の場合

    EvaluateCall

    AO:

    3
    • 通常の関数呼び出し 3
    • コンストラクター呼び出し
      • 実際の関数呼び出しは、
      • Call
      AO で発生し、thisValue を使用する呼び出しのコンテキストによって決まります
    • このパラメーターは、呼び出しに関連する長い呼び出しチェーンで渡されます。
    Call

    [[Call]] 関数を呼び出すための内部スロット。これにより、PrepareForOrdinaryCall が呼び出され、新しい 関数環境レコード が作成されます。 さらに、関数環境レコードには [[ThisValue]] フィールドもあります: NewFunctionEnvironment この呼び出しでは、関数環境の [[ThisBindingStatus]] プロパティも設定します。

    [[Call]]

    OrdinaryCallBindThis も呼び出します。ここで、適切な

    thisArgument

    は次の内容に基づいて決定されます。

    確認後、新しく作成された関数環境レコードの BindThisValue メソッドが最終的に呼び出され、実際に [[ThisValue]] フィールドが thisArgument に追加されるように設定されます。

    最後に、このフィールドは 関数環境レコード です。 GetThisBinding AO は、次の場所から this の値を取得します。

    繰り返しますが、this 値の正確な決定は多くの要因に依存します。これは単なる一般的な概要です。このような技術的な背景を踏まえて、具体的な例をすべて見てみましょう。

    アロー関数

    アロー関数 を評価するとき、[[ThisMode]] 内部スロット関数オブジェクトのプロパティは、OrdinaryFunctionCreate で「lexical」に設定されます。 p>

    OrdinaryCallBindThis では、関数 F:

    を受け取ります。

    これは単に、残りのアルゴリズム バインディング this がスキップされることを意味します。アロー関数は、独自の this 値をバインドしません。

    それでは、アロー関数の this とは何でしょうか? ResolveThisBindingGetThisEnvironment を思い出すと、HasThisBinding メソッドは明示的に false を返します。

    したがって、私たちは外部環境を繰り返し探します。このプロセスは、this バインディングを持つ 3 つの環境のいずれかで終了します。

    これは、アロー関数本体の this がアロー関数の語彙スコープ から、つまり ( アロー関数と. 関数宣言/式 式: これらは同等/交換可能ですか?):

    関数プロパティ

    通常の関数 (functionmethod) では、this 関数呼び出しメソッドによって決まります。

    ここで、これらの「構文のバリエーション」が役に立ちます。

    関数を含むこのオブジェクトについて考えてみましょう:

    リーリー ###または:###リーリー

    次の関数呼び出しでは、func 内の this の値は refObj になります。 1< /p>

    • refObj.func()
    • refObj["関数"]()
    • refObj?.func()
    • refObj.func?.()
    • refObj.func``

    呼び出された関数が構文的に基本オブジェクトのプロパティである場合、その基本オブジェクトは呼び出しの「参照」となり、通常の場合は this の値になります。これについては、上にリンクされている評価手順で説明しています。たとえば、refObj.func() (または refObj["func"]()) の CallMemberExpression は、式 refObj.func() 全体です。これは、MemberExpression refObj.func# で構成されます。 ##パラメーター###### ###()###。 さらに、refObj.funcrefObj

    はそれぞれ 3 つの役割を果たします:

    これらはすべて式です、 これらはすべて参考資料であり、

      それらはすべて値です。
    • refObj.func
    は呼び出し可能な関数オブジェクトであり、対応する

    参照 this バインディングを決定するために使用されます。 オプションのリンクとタグ テンプレートの例は非常によく似ています。基本的に、参照は ?.() 前、``

    前、または

    () EvaluateCall<代码> IsPropertyReference

    を使用して、それが構文的にオブジェクトのプロパティであるかどうかを判断します。参照の [[Base]] プロパティを取得しようとします (例:

    refObj.func に適用される場合は refObj、適用される場合は foo.bar code> foo.bar.baz)。プロパティとして記述された場合、GetThisValue はこの [[Base]] プロパティを取得し、それを this 値として使用します。 注: Getters / Setters の作業メソッドとメソッド (this

    に関する)。単純なプロパティは、グローバル スコープの

    this のように、実行コンテキストには影響しません。 リーリー 基本参照なし、厳密モード、および with

    基本参照のない呼び出しは、通常、属性として呼び出されない関数です。例えば:### リーリー これは、 メソッド < 的调用/代码> を渡すか代入する場合、または コンマ演算子

    を使用する場合にも発生します。ここで、参照レコードと値の違いが関係します。

    関数

    j についての注意: 仕様によれば、j は参照レコードではなく、関数オブジェクト (値) 自体のみを返すことができることに注意してください。したがって、ベース参照 refObj

    が失われます。

    リーリー

    EvaluateCall Call を呼び出します。ここで、thisValue未定義です。これは、 OrdinaryCallBindThis (F: 関数オブジェクト; thisArgument: thisValueCall に渡される) では異なります。 # < /p>注: ステップ 5 では、

    this の実際の値を、厳密モードで提供される thisArgument (この場合は unknown) に設定します。 「ずさんなモード」では、未定義または null thisArgument により、this がグローバル this 値になります。

    IsPropertyReference

    false を返した場合、EvaluateCall は次の手順を実行します。 これは未定義です thisValue

    から取得される可能性があります:

    refEnvWithBaseObject() は、 を除き、常に unknown です。 -evaluation" rel="noreferrer">withSymbol.unscopables (Documentation on MDN) もあります。 これまでの内容を要約すると: リーリー ###そして:### リーリー

    this

    を計算する場合、

    通常の関数が定義されている位置は問題ではないことに注意してください。

    .call

    .apply

    .bindthisArg とプリミティブ OrdinaryCallBindThis ステップ 5 のもう 1 つの結果は、(仕様の) ステップ 6.2 とは異なり、「sloppy」モード の元の this 値は

    のみであるということです。オブジェクトにキャストします。

    これを確認するために、this 値の別のソースを導入しましょう。this バインディングをオーバーライドする 3 つのメソッド: 4

    Function.prototype.apply(thisArg, argArray)

    Function.prototype.
      {
    • Call, Bind
    • }
    • (thisArg, ...args) .bind バインド関数を作成します。その this
    バインディングはすでに

    thisArg に設定されており、再度変更することはできません。 .call および .apply は関数をすぐに呼び出し、thisthisArg にバインドするように設定します。 ###。

    .call.apply は、指定された thisArg を使用して、Call に直接マッピングされます。 .bind BoundFunctionCreate を使用して、バインドされた関数を作成します。これらには独自の [[Call ]] メソッド があり、関数オブジェクトの [[BoundThis]] 内部スロットを検索します。

    カスタム this 値の設定例:

    リーリー

    オブジェクトの場合、これは厳密モードでも非厳密モードでも同じです。

    ここで、プリミティブ値を指定してみます:

    リーリー

    非厳密モードでは、プリミティブはオブジェクト ラッパー形式に強制されます。これは、Object("s") または new String("s") を呼び出したときに取得するオブジェクト型と同じです。厳密モードでは、 プリミティブを使用できます: リーリー

    これらのメソッドを利用するライブラリ (jQuery など) は、ここで選択した DOM 要素に

    this を設定します: リーリー

    コンストラクター、

    クラスおよびNew

    new 演算子を使用して関数がコンストラクターとして呼び出される場合、EvaluateNewConstruct を呼び出し、これにより [[Construct]] メソッドが呼び出されます。 ###。関数が基本コンストラクターである場合 (つまり、class extends...{...} ではない)、thisArgument を次のように設定します。コンストラクターのプロトタイプから作成された新しいオブジェクト。コンストラクターの this に設定されたプロパティは、最終的に生成されたインスタンス オブジェクトに表示されます。 this は、独自の非プリミティブ値を明示的に返さない限り、暗黙的に返されます。

    class は、ECMAScript 2015 で導入された、コンストラクターを作成する新しい方法です。 リーリー クラス定義は暗黙的に

    strict モード

    : リーリー #########素晴らしい#########

    new

    動作の例外は、上で説明した class extends...{...}

    です。派生クラスは、呼び出されてすぐに 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% 組み込みオブジェクトである必要があります。 3

    : 仕様に「

    ref を式の評価結果とする。」と記載されている場合は常に、たとえば、MemberExpression または CallExpression の評価は、これらのアルゴリズムです。これらの一部は 参照レコード を生成します。 4: this 値を指定できるネイティブ メソッドとホスト メソッドが他にもいくつかあります (特に

    Array.prototype.map

    Array .prototype)。 .forEach などは thisArg を 2 番目の引数として受け入れます。誰もが this を変更する独自のメソッドを作成できます (例: < code>(func, thisArg) => func.bind(thisArg), (func, thisArg) => func 。 call(thisArg) など。いつものように、MDN は優れたサービス ドキュメントを提供します。 お楽しみのために、いくつかの例で理解をテストしてください 各コード スニペットについて、次の質問に答えてください: 「マークされた行の

    this
    の値は何ですか?なぜですか?」

    .

    答えを表示するには、灰色のボックスをクリックします。

    1. リーリー

      グローバルこれ。マークされた行は、初期のグローバル実行コンテキスト内で評価されます。

    2. リーリー

      返事
      0
  • キャンセル返事