「this」キーワードはどのように機能し、いつ使用すればよいのでしょうか? 質問する

「this」キーワードはどのように機能し、いつ使用すればよいのでしょうか? 質問する

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

動作がおかしいようですが、その理由がよくわかりません。

どのようthisに機能し、いつ使用すればよいのでしょうか?

ベストアンサー1

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

this仕様書の技術的説明

ECMAScript 標準定義するthis抽象操作(略称AO)を介してこのバインディングを解決する:

this[AO] ResolveThisBinding […]は、LexicalEnvironmentを使用してキーワードのバインディングを決定します。実行コンテキストの実行[手順]:

  1. envRecこの環境を取得する()。
  2. 戻り値: 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 具象メソッドは[…] [次の処理を実行します]:

  1. envRec .[[GlobalThisValue]]を返します。

グローバル環境レコードの[[GlobalThisValue]]プロパティは常にホスト定義に設定されます。グローバルオブジェクト、これは以下からアクセス可能ですglobalThis( windowWeb 上、globalNode.js 上;MDN のドキュメント)。以下の手順に従ってください。ホスト定義領域の初期化[[GlobalThisValue]] プロパティがどのように生成されるかを学びます。

2. グローバル実行コンテキストモジュール

モジュールは ECMAScript 2015 で導入されました。

<script type="module">これは、単純な ではなく、の直接内部にある場合など、モジュールに適用されます<script>

モジュールの初期グローバル実行コンテキストでは、評価thisによってGetThisBinding以下の手順を実行します。

モジュール Environment Record の GetThisBinding 具象メソッドは、次の処理を実行します。

  1. 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通話が直接か間接かによって異なります。

つまり、

  • 直接的な 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を受け取ります。

  1. thisModeをF .[[ThisMode]]します。
  2. thisModeがlexical場合は、NormalCompletion( undefined) を返します。[…]

これは、 thisをバインドするアルゴリズムの残りの部分がスキップされることを意味します。矢印関数は、自身のthis値をバインドしません。

では、this矢印関数の中身は何なのでしょうか?このバインディングを解決するそしてこの環境を取得するHasThisBindingメソッドは明示的にfalseを返します

関数 Environment Record envRecの HasThisBinding 具象メソッドは[…] [次のことを行います]:

  1. 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.funcrefObj

  • どちらも表現です
  • どちらも参考資料であり、
  • どちらも価値あるものです。

refObj.func値は呼び出し可能な関数オブジェクトであり、対応する参照がバインディングを決定するために使用されますthis

オプションの連鎖とタグ付きテンプレートの例は非常によく似た動作をします。基本的に、参照は の前?.()、 の前``、または の前のすべてです()

評価通話用途プロパティ参照参照の [[Base]] プロパティを取得しようとしています (たとえばrefObj、 に適用された場合はrefObj.funcfoo.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わかります。そのため、基本参照は失われます。jrefObj

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.

評価通話通話電話ここではthisValueundefinedです。これにより通常呼び出しバインド( F : 関数オブジェクト; thisArgument :渡されるthisValue電話):

  1. thisModeをF .[[ThisMode]]します。

[…]

  1. thisModestrictの場合thisValue をthisArgumentにします
  2. それ以外、
    1. thisArgumentundefinedまたはnullの場合
      1. globalEnvcalleeRealm .[[GlobalEnv]]とします。
      2. […]
      3. thisValueglobalEnv .[[GlobalThisValue]]とします。
    2. それ以外、
      1. thisValueをとします。物申す(この引数)。
      2. 注記:物申すラッパー オブジェクトを生成します […]。

[…]

注:この場合、ステップ 5 では、厳密モードで this指定されたthisArgumentに実際の値が設定されます。「sloppy モード」では、未定義または null のthisArgumentがグローバルthis値になりますundefinedthis

もしプロパティ参照falseを返す場合、評価通話次の手順に従います。

  1. refEnvref .[[Base]]とします。
  2. アサート: refEnvは環境レコードです。
  3. thisValue をrefEnv .WithBaseObject()にします

未定義のthisValueが返される可能性があるのは、refEnvです。ベースオブジェクト() は常にundefinedですwithステートメント。この場合、thisValue がバインディング オブジェクトになります。

また、Symbol.unscopablesMDN のドキュメント) を使用して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.bindthisArg、およびプリミティブ

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.

  1. if(true){
      console.log(this); // What is `this` here?
    }
    

    globalThis. The marked line is evaluated in the initial global execution context.

  2. 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 reference obj.method, i.e. obj.

  3. 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 value myFun / obj.myMethod is not called off of an object, as a property, the this binding will be globalThis. This is different from Python, in which accessing a method (obj.myMethod) creates a bound method object.

  4. 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, so this is globalThis.

  5. function myFun(){
      console.log(this); // What is `this` here?
    }
    
    const obj = {
        myMethod: function(){
          eval("myFun()");
        }
      };
    
    obj.myMethod();
    

    globalThis直接の eval 呼び出しを評価する場合、thisは ですobj。ただし、 eval コードでは、myFunはオブジェクトから呼び出されないため、thisバインディングはグローバル オブジェクトに設定されます。

  6. 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.callthisArg

  7. 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());
    
       

    これは のインスタンスです。矢印関数はthisMyClsバインディングを変更しないため、これはレキシカル スコープから取得されます。したがって、これはのような上記のクラス フィールドとまったく同じです。これを に変更してみてください。期待どおりの結果が得られますか?a = this;static arrow

おすすめ記事