JavaScriptのポイントごとのシリーズでこれは何ですか

JavaScriptのポイントごとのシリーズでこれは何ですか

これを理解する

おそらく、他のオブジェクト指向プログラミング言語でもthis見たことがあり、これがコンストラクターによって作成されたオブジェクトを参照することをご存知でしょう。しかし実際には、JavaScript では、 this作成されたオブジェクトだけを表すものではありません。

まず、ECMAScript 標準仕様におけるこの定義を見てみましょう。

「this キーワードは、現在の実行コンテキストの ThisBinding の値を評価します。」
「このキーワードは、現在の実行コンテキストの ThisBinding の値を表します。」

次に、MDN の定義を見てみましょう。

「ほとんどの場合、この値は関数の呼び出し方法によって決まります。」
「ほとんどの場合、この値は関数の呼び出し方法によって異なります。」

さて、上記の 2 行を理解できる場合は、読み続ける必要はありません。おめでとうございます!

…そうは思いません。少なくともこの2行だけを見てもまだ分かりません。

例を見てみましょう:

var getGender = 関数() {
    people1.gender を返します。
};

var 人1 = {
    性別: '女性'、
    性別を取得: 性別を取得
};

var 人々2 = {
    性別: '男性'、
    性別を取得: 性別を取得
};

console.log(people1.getGender()); // 女性
console.log(people2.getGender()); // 女性

何ですか? people2 はなぜ性別を変えたのですか? これは私が望んでいる結果ではありません。なぜですか?

getGender()people1.genderを返すため、結果は当然「女性」になります。

したがって、 getGender少し変更すると、次のようになります。

var getGender = 関数() {
    this.gender を返します。
};

この時点で、 femalemale 2 つの結果が返されるはずです。

先ほど述べた点に戻ると、この例から、 people1people2getGenderメソッドは同じ getGender 関数を参照しているにもかかわらず、呼び出されるオブジェクトが異なるため、実行結果が異なることがわかります

これで、最初の重要なポイントがわかりました。**this は実際には関数が呼び出されたときにバインドされ、それが指すものは関数の呼び出し方法によって完全に異なります。 **これをどう区別するのでしょうか?

これは誰ですか

上記の例を読んでも、まだ混乱しているように感じますか?次に、さまざまな呼び出し方法がこの値に与える影響を見てみましょう。

ケース1: グローバルオブジェクトと通常の関数の呼び出し

グローバル環境では、これはグローバル オブジェクトを指し、ブラウザーではウィンドウ オブジェクトを指します。次の例では、厳密モードであるかどうかに関係なく、これはグローバル オブジェクトを参照します。

変数x = 1

コンソールログ(this.x) // 1
console.log(this.x === x) // 真
console.log(this === window) // true

非厳密モードでグローバル環境で通常の関数が呼び出された場合、通常の関数内の this もグローバル オブジェクトを参照します。厳密モードでは、 this は未定義になります。 ES5 では、JavaScript をより制限の厳しい環境で実行するための厳密モードが追加されました。セキュリティ リスクを排除するために、厳密モードでは this キーワードがグローバル オブジェクトを指すことが禁止されます。

変数x = 1

関数fn() {
    console.log(this); // ウィンドウのグローバルオブジェクト console.log(this.x); // 1
}

関数fn();

厳密モードを使用した後:

"use strict" // 厳密モードを使用する var x = 1

関数fn() {
    console.log(this); // 未定義
    console.log(this.x); // this は未定義なので、「未定義のプロパティ 'x' を読み取れません」と報告します
}

関数fn();

ケース2: オブジェクトメソッドとして呼び出す

オブジェクト内の値がプリミティブ型 (たとえば、文字列、数値、ブール値) である場合、この新しく作成されたものを「プロパティ」と呼びます。また、オブジェクト内の値が関数である場合、この新しく作成されたものを「メソッド」と呼びます。

関数がオブジェクトのメソッドであり、オブジェクトのメソッドとして呼び出される場合、関数内の this は親オブジェクトを指します

変数x = 1
var obj = {
    x: 2,
    関数: 関数() {
        console.log(これを);    
        コンソールにログ出力します。
    }
}

obj.fn()     

// obj.fn() は結果を出力します。
// オブジェクト {x: 2, fn: 関数}
// 2

var a = obj.fn
()   

// a() は結果を出力します:   
// ウィンドウグローバルオブジェクト // 1

上記の例では、obj.fn() を直接実行しています。関数の親オブジェクトは obj なので、this は obj を指し、this.x の値は 2 です。次に、最初に fn メソッドを変数 a に割り当てます。a はグローバル環境で実行されるので、this はグローバル オブジェクト Window を指し、this.x の値は 1 です。

別の例を見てみましょう。関数がネストされた複数のオブジェクトによって呼び出された場合、これは何を指すのでしょうか?

変数x = 1
var obj = {
  x: 2,
  年: {
    x: 3,
    関数: 関数() {
      console.log(this); // オブジェクト {x: 3, fn: function}
      console.log(this.x); // 3
    }
  }
}

obj.y.fn();

なぜ結果が 2 ではないのでしょうか? この場合、1 つの文を覚えておいてください。これは常に、関数を直接呼び出す親オブジェクト、つまり y を指します。上記の例では、実際には次のコードが実行されます。

var y = {
  x: 3,
  関数: 関数() {
    console.log(this); // オブジェクト {x: 3, fn: function}
    console.log(this.x); // 3
  }
}

変数x = 1
var obj = {
  x: 2,
  y: y
}

obj.y.fn();

オブジェクトはネストすることができ、関数も同様にネストできます。関数がネストされると、これは変わりますか?次のコードでこれを調べてみましょう。

var obj = {
    y: 関数() {
        console.log(this === obj); // 真
        console.log(this); // オブジェクト {y: function}
        関数fn();

        関数fn() {
            console.log(this === obj); // 偽
            console.log(this); // ウィンドウのグローバル オブジェクト}
    }
}

obj.y();

関数 y では、this はそれを呼び出す親オブジェクト obj を指しており、これは問題ありません。しかし、ネストされた関数 fn では、これは obj を指しません。ネストされた関数は、それを呼び出す関数から this を継承しません。ネストされた関数が関数として呼び出されると、その this 値は非厳密モードではグローバル オブジェクトを指し、厳密モードでは未定義になります。したがって、上記の例では実際には次のコードが実行されます。

関数fn() {
    console.log(this === obj); // 偽
    console.log(this); // ウィンドウのグローバル オブジェクト}

var obj = {
    y: 関数() {
        console.log(this === obj); // 真
        console.log(this); // オブジェクト {y: function}
        関数fn();
    }
}

obj.y();

ケース3: コンストラクターとして呼び出される

new キーワードを使用して、コンストラクターを通じてインスタンス オブジェクトを生成できます。この時点で、 this は新しいオブジェクトを参照します

var x = 1;

関数Fn() {
 2 を 0 にします。
    console.log(this); // 関数 {x: 2}
}

var obj = new Fn(); // Fn(..) 呼び出しで obj をこれにバインドします console.log(obj.x) // 2

Fn(..) newで呼び出すと、新しいオブジェクトが構築され、 Fn(..)呼び出しでそのオブジェクト (obj) が this にバインドされます。また、コンストラクターが非参照型 (文字列、数値、ブール値、null、未定義) を返す場合でも、インスタンス化された新しいオブジェクトを指すことにも注意してください。

変数x = 1

関数Fn() {
  これ.x = 2

  戻る {
    ×: 3
  }
}

var a = 新しいFn()

コンソール.log(ax) // 3

Fn() はオブジェクト (参照型) を返すため、これは返されたオブジェクトを指します。戻り値が非参照型の場合はどうなりますか?

変数x = 1

関数Fn() {
  これ.x = 2

  戻る 3
}

var a = 新しいFn()

コンソールログ(ax) // 2

ケース4: call メソッド呼び出しと apply メソッド呼び出し

これが参照するものを変更する場合は、 call または apply メソッドを使用できます。最初のパラメータは、関数の実行時にthisが指す場所を指定します。最初のパラメータが渡されない場合 (パラメータが空の場合)、または null または undefined が渡された場合、デフォルトでは this はグローバル オブジェクト (非厳密モード) または undefined (厳密モード) を指します。

var x = 1;

var obj = {
  ×: 2
}

関数fn() {
    console.log(これを);
    コンソールにログ出力します。
}

fn.call(オブジェクト)
// オブジェクト {x: 2}
// 2

fn.apply(オブジェクト)     
// オブジェクト {x: 2}
// 2

fn.call()         
// ウィンドウグローバルオブジェクト // 1

fn.apply(null)    
// ウィンドウグローバルオブジェクト // 1

fn.call(未定義)    
// ウィンドウグローバルオブジェクト // 1

call と apply を使用する場合、 this に渡される値がオブジェクトでない場合、JavaScript は関連するコンストラクターを使用してそれをオブジェクトに変換します。たとえば、数値型が渡されると、 new Number()操作が実行され、文字列型が渡されると、 new String()操作が実行され、ブール型が渡されると、新しい Boolean() 操作が実行されます。

関数fn() {
  console.log(Object.prototype.toString.call(this))
}

fn.call('love') // [オブジェクト文字列]
fn.apply(1) // [オブジェクト番号]
fn.call(true) // [オブジェクト ブール値]

call と apply の違いは、call の 2 番目以降のパラメータがパラメータ リストであるのに対し、apply の 2 番目のパラメータは配列である点です。パラメータ リストとパラメータ配列はどちらも関数への引数として実行されます。

変数x = 1

var obj = {
  ×: 2
}

関数Sum(y, z) {
  コンソールログ(this.x + y + z)
}

Sum.call(obj, 3, 4) // 9
合計を適用(obj、[3, 4]) // 9

ケース5: バインドメソッド呼び出し

f.bind(someObject) を呼び出すと、 f と同じ本体とスコープを持つ関数が作成されますが、この新しい関数では、関数がどのように呼び出されたかに関係なく、新しい関数の this は、 bind によって渡された最初の引数を永続的にポイントします

変数x = 1

var obj1 = {
    ×: 2
};
var obj2 = {
    ×: 3
};

関数fn() {
    console.log(これを);
    コンソールにログ出力します。
};

var a = fn.bind(obj1);
var b = a.bind(obj2);

関数fn();
// ウィンドウグローバルオブジェクト // 1

();
// オブジェクト {x: 2}
// 2

関数b();
// オブジェクト {x: 2}
// 2

a.call(obj2);
// オブジェクト {x: 2}
// 2

上記の例では、this ポインターを関数 a に再割り当てしようとしていますが、このポインターは最初の bind によって渡されたオブジェクトを依然として指しています。call メソッドまたは apply メソッドを使用しても、この事実は変更できません。つまり、このポインターは、bind によって渡された最初のパラメーターを永続的に指し続けます。

ケース6: thisは矢印関数で指している

ES6 以降ではアロー関数が追加されていることも注目に値します。MDN のアロー関数の説明を見てみましょう。

矢印関数式は関数式よりも短い構文を持ち、独自のthisargumentssuper 、またはnew.targetをバインドしません。矢印関数は常に匿名です。これらの関数式は非メソッド関数に最適であり、コンストラクターとして使用することはできません。

ここでは、矢印関数には独自のthisバインディングがないことが明確に述べられています。矢印関数内で使用されるthis 、実際にはそれを直接含む関数または関数式内のthisです。前のケース 2 のネストされた関数の例では、ネストされた関数は上位関数の this を継承しません。矢印関数を使用するとどのような変化が起こりますか?

var obj = {
  y: 関数() {
        console.log(this === obj); // 真
        console.log(this); // オブジェクト {y: function}

      var fn = () => {
          console.log(this === obj); // 真
          console.log(this); // オブジェクト {y: function}
      }
      関数fn();
  }
}

obj.y()

通常の関数と異なり、矢印関数内の this は obj を指しています。これは、前の層の関数から this を継承しているためです。矢印関数は this の方向を修正していることがわかります。したがって、矢印関数の this は呼び出されたときに決定されるのではなく、定義されているときのオブジェクトがその this になります

つまり、矢印関数の this は、外側のレイヤーに関数があるかどうかによって決まります。外側のレイヤーに関数がある場合、外側の関数の this は内側の矢印関数の this になります。外側のレイヤーに関数がない場合、 this は window になります

var obj = {
  y: () => {
        console.log(this === obj); // 偽
        console.log(this); // ウィンドウのグローバルオブジェクト var fn = () => {
          console.log(this === obj); // 偽
          console.log(this); // ウィンドウのグローバル オブジェクト}
      関数fn();
  }
}

obj.y()

上記の例では、矢印関数が 2 つありますが、これは実際には最も外側の矢印関数に依存します。obj は関数ではなくオブジェクトであるため、これは Window グローバル オブジェクトを指します。

bind と同様に、矢印関数も非常に「頑固」です。call や apply を通じて this の方向を変更することはできません。つまり、渡された最初のパラメータは無視されます

変数x = 1
var obj = {
    ×: 2
}

var a = () => {
    コンソールログ(this.x)
    console.log(これ)
}

a.call(オブジェクト)       
// 1
// ウィンドウのグローバルオブジェクト a.apply(obj)      
// 1
// ウィンドウのグローバルオブジェクト

上記の説明は文字が多すぎて少し味気ないかもしれませんので、次のフローチャートを見てみましょう。この図は非常によくまとめられていると思います。図のプロセスは単一のルールにのみ適用されます。

まとめ

この記事では、これが指すいくつかの状況を紹介します。動作環境や呼び出し方法によって、これが影響を受けます。一般に、関数の this の参照は、現在関数を呼び出しているオブジェクト、つまり実行時のオブジェクトに依存します。このセクションでは、以下の内容を習得する必要があります。

  • これはグローバル オブジェクトを参照します。
  • 厳密モードと非厳密モードでのこの違いは次のとおりです。
  • 関数がオブジェクト メソッドとして呼び出される場合を指すいくつかの状況。
  • コンストラクターとして使用された場合に this が指すものと、それが返すものの違い。
  • 関数を呼び出すオブジェクトを変更するには、call と apply を使用します。
  • bind によって作成された関数内の this の参照。
  • 矢印関数内の this ポインター。

これで、JavaScript ブレークスルー シリーズの「これとは何か」に関する記事は終了です。JavaScript に関するこのコンテンツの詳細については、123WORDPRESS.COM で以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • このリファレンスとJavaScriptのカスタムプロパティの詳細な説明
  • JavaScript における this ポイントの問題の詳細な説明
  • 関数の分類の詳細な説明とJavascriptでのこのポイントの例
  • JavaScript関数におけるこのポイントの問題の詳細な説明
  • JavaScriptにおけるこのポインティング問題の詳細な説明
  • JS にこれがあるのはなぜですか?

<<:  MySQLでよく使われる演算子と関数の概要

>>:  2時間のDocker入門チュートリアル

推薦する

中国語でのNginx設定パラメータの詳細な説明(負荷分散とリバースプロキシ)

PS: 最近、nginx を詳細に紹介している <<High-Performance ...

JavaScript インスタンス オブジェクトでプロトタイプ メソッドをオーバーライドする方法の詳細

目次JavaScriptでは、通常、次のコードのようにクラスを簡単に定義できます。 var サンプル...

Reactは適応性の高い仮想リストを実装する

目次変換前:変換後: 0x0の基本0x1 「固定高さ」の仮想リストを実装する原理:最適化: 0x2 ...

MySQLの読み書き分離により挿入後にデータが選択されなくなる問題を解決

MySQLは独立した書き込み分離を設定します。コードに次のものを書くと問題が発生する可能性があります...

JS、CSS スタイルのリファレンスの記述

CS: ... 1. <link type="text/css" href...

Vue パッケージアップロードサーバー更新 404 問題に対する 2 つの解決策

1: nginxサーバーソリューション、.conf構成ファイルを変更する解決策は2つある1: 位置 ...

Vue はタブ ラベルを実装します (ラベルが自動スクロールを超える)

作成されたタブラベルがページの表示領域を超えると、タブラベルの距離だけ自動的にスクロールされます。ま...

React Nativeでシンプルなゲームエンジンを作る

目次導入始めるReact Nativeゲームエンジンの簡単な紹介React Nativeでスネークゲ...

Reactフックの長所と短所

目次序文アドバンテージ:欠点: 1. レスポンシブな使用効果2. ステータスが同期されていないRea...

CentOS システムのディスク パーティションを拡張する方法

問題/障害/シナリオ/要件Eve-ng の仮想マシン OVA のハードディスクは 38G しかないた...

Docker での RocketMQ の詳細なインストールと使用

RocketMQ イメージを検索するには、Docker の hub.docker.com で検索する...

左右の幅を固定し、中央の幅を適応させたHTMLレイアウトのソリューションの詳細な説明

この記事では、次のように、誰にでも共有できる左右幅固定のミドルアダプティブ HTML レイアウトソリ...

Vue で Axios カプセル化を使用するための完全なチュートリアル

序文現在、プロジェクトでは、Axios ライブラリが HTTP インターフェース リクエストによく使...

docker runとstartの違い

docker における実行と開始の違いDocker run はミラーイメージを指定します。そしてdo...

5分でDockerをインストールする詳細な手順

CentOS に Docker をインストールするには、オペレーティング システムが CentOS ...