JavaScriptにおけるこれの深い理解

JavaScriptにおけるこれの深い理解

Jsでのこれの深い理解

JavaScriptスコープはstatic scopeスコープですが、 Jsthisは例外です。 thisのポイントの問題は動的スコープに似ています。関数とスコープがどのように、どこで宣言されるかは関係なく、どこから呼び出されるかだけを関係します。関数が定義されているときは、 thisのポイントを決定できません。関数が実行されたときにのみ、 this実際に誰を指しているかを決定できます。もちろん、実際には、 this最終的に、それを呼び出すオブジェクトを指します。

範囲

なぜthisが動的スコープに似ているのかを理解するために、まずJavaScriptのスコープについて理解しましょう。一般的に、プログラム コード内で使用される名前は常に有効または使用可能であるわけではなく、この名前の可用性を制限するコードのスコープがこの名前のscopeです。メソッドまたはメンバーが宣言されると、現在の実行contextが設定されます。特定の値を持つcontextでは、式は表示され、参照できます。変数またはその他の式が現在のスコープ内にない場合、使用できません。スコープは階層化することもでき、通常はスコープのチェーンをたどることで子スコープが親スコープにアクセスできるようになりますが、子スコープ内の変数と参照は親スコープから参照できません。
JavaScriptスコープはstatic scopeであり、 lexical scopeスコープとも呼ばれます。その主な特徴は、関数スコープが、パラメータでも関数内で定義されていないローカル変数に遭遇すると、関数定義のコンテキストで検索することです。対照的に、 dynamic scope異なります。関数スコープが、パラメータでも関数内で定義されていないローカル変数に遭遇すると、関数呼び出しのコンテキストで検索します。

var a = 1;
var s = 関数(){
 コンソールにログ出力します。
};
(関数(){
 var a = 2;
 s(); // 1
})();

s() が呼び出されると、a は 1 として出力されます。これは静的スコープです。つまり、スコープは宣言時に指定されます。動的スコープの場合は、ここに 2 が出力されます。現在、 CC++JavaPHPPythonなど、ほとんどの言語は静的スコープを使用しています。動的スコープを持つ言語には、 Emacs LispCommon LispPerlなどがあります。

グローバルスコープ

最上位レベルで直接宣言された変数またはメソッドは、グローバル スコープで実行されます。スコープを表示するには、関数の[[Scopes]]プロパティを使用します。 [[Scopes]]は、関数のスコープ チェーンを格納するオブジェクトです。これは関数の内部プロパティであり、直接アクセスすることはできませんが、表示のために印刷することはできます。

関数 s(){}
console.dir(s);
/*
 ...
 [[スコープ]]: スコープ[1]
 0: グローバル...
*/
// 宣言された s 関数がグローバル スコープで実行されることがわかります。

関数のスコープ

関数が宣言されると、関数内で宣言されたメソッドまたはメンバーの動作環境が関数の関数スコープになります。

(関数localContext(){
 var a = 1;
 関数 s(){ return a; }
 console.dir(s);
})();
/*
 ...
 [[スコープ]]: スコープ[2]
 0: クロージャ (localContext) {a: 1}
 1: グローバル...
*/
// 宣言された s 関数が実行されるコンテキストは関数のスコープ localContext であり、ローカル スコープとも呼ばれることがわかります。

ブロックスコープ

コード ブロック内にletまたはconstがある場合、コード ブロックは、ブロックの先頭からこれらのコマンドによって宣言された変数に対して閉じたスコープを形成します。

{
 a = 1 とします。
 関数 s(){戻り値 a;}
 console.dir(s);
 /*
 ...
 [[スコープ]]: スコープ[2]
 0: ブロック {a: 1}
 1: グローバル...
 */
}
// 宣言された s 関数がブロック スコープ (ローカル スコープでもある) で実行されることがわかります。

分析する

this this JavaScriptで設計されている理由を理解する必要があります。その前に、小さな例を見てみましょう。通常、 this使用するときに遭遇する可能性のある典型的な問題は、次のようになります。同じ関数を実行しているにもかかわらず、実行結果が異なる場合があります。

var obj = {
 名前: 1,
 言う: function() {
 this.name を返します。
 }
};
ウィンドウ名 = 2;
ウィンドウを言う = obj.say;

コンソールログ(obj.say()); // 1
コンソールログ(window.say()); // 2

この結果の理由は、 thisキーワードの使用です。前述のように、 this実行時に決定する必要があります。ここで、 obj.say()の場合、 say()が実行される環境はobjオブジェクトであり、 window.say()の場合、 say()が実行される環境はwindowオブジェクトであるため、2 つの操作の結果は異なります。
では、なぜJavaScript thisような設計になっているのかを理解しましょう。まずはJavaScriptのメモリ構造におけるスタックについて理解しましょう。 heapは不確定なサイズで動的に割り当てられるメモリであり、自動的に解放されることはありません。 stack自動的に割り当てられるメモリ空間であり、コード実行中に自動的に解放されます。 JavaScript 、スタック メモリ内でのJsコード実行環境を提供します。すべてのスコープと関数呼び出しはスタック メモリ内で実行されます。 Jsの基本データ型であるStringNumberBooleanNullUndefinedSymbolは、占有するスペースが小さく、サイズが固定されています。値はスタック メモリに直接格納され、値によってアクセスされます。 Object参照型の場合、そのポインタはスタック メモリに配置され、ヒープ メモリの実際のアドレスを指し、参照によってアクセスされます。
さて、上の例を見てみましょう。メモリでは、 objオブジェクトはヒープメモリに格納されています。オブジェクト内の属性値が基本データ型の場合、オブジェクトと同じメモリ領域に格納されますが、この属性値は参照型である可能性もあるため、 say関数もヒープメモリに存在します。実際には、ここでは、この関数の実際の定義がメモリ領域(匿名関数の形で存在)にあると理解でき、 objオブジェクトも別のメモリ領域にあります。obj objsay属性を介してこの匿名関数のメモリアドレスを指し示します( obj --say--> funtion 。ここで問題が発生します。このメモリ構造により、任意の変数オブジェクトがこの関数を指すようにすることができます。そのため、 JavaScript関数では、使用するために実行環境の値を取得できるようにする必要があります。関数本体内で現在の実行環境contextを取得するメカニズムが必要なため、 this表示されます。その設計目的は、関数本体内で関数の現在の実行環境を参照することです。

使用

this定義時ではなく実行時にバインドされることを覚えておく必要があります。そのcontext 、関数が呼び出されたときのさまざまな条件に依存します。簡単に言うと、 thisのバインドは関数宣言の場所とは関係なく、関数の呼び出し方法のみに依存します。簡単に言うと、 this矢印関数を除いて常に呼び出し元を指します。次に、 thisの 5 つの使用法を紹介します。

デフォルトのバインディング

最もよく使われる関数呼び出しタイプは独立関数呼び出しであり、これは最も優先順位が低いものでもあります。このとき、 thisグローバルオブジェクトを指します。 strict modeを使用すると、グローバルオブジェクトはデフォルトのバインディングを使用できなくなるため、 this undefinedになることに注意してください。

var a = 1; // グローバルオブジェクト内の変数宣言 function f1() {
 this.a を返します。
}

関数f2() {
 「厳密な使用」;
 これを返します。
}

console.log(f1()); // 1 // 実際にはwindow.f1()を呼び出しており、これは常に呼び出し元、つまりwindowを指します。
console.log(f2()); // undefined // 実際にはwindow.f2()を呼び出します。この時点では、strictモードuse strictのため、関数内のthisはundefinedです。

暗黙のバインディング

オブジェクト プロパティ参照チェーンの最上位または最後のレイヤーのみがthisに影響します。同様に、 this常に呼び出し元を指します。具体的には、矢印関数を除き、最新の呼び出し元を指す必要があります。また、意図的または無意識のうちに間接参照を作成する場合もあります。この場合、 this呼び出し元にも適用されます。上記の分析で使用した例は、間接参照の場合に属します。

関数f(){
 コンソールにログ出力します。
}
var obj1 = {
 a: 1、
 ファ: ファ
};
var obj2 = {
 11,
 オブジェクト1: オブジェクト1
};
obj2.obj1.f(); // 1 // 呼び出し元の最後の層はobj1です
関数f(){
 コンソールにログ出力します。
}
var obj1 = {
 a: 1、
 ファ: ファ
};
var obj2 = {
 11,
};
obj2.f = obj1.f; // 間接参照 obj2.f(); // 11 // 呼び出し元は obj2 です

ディスプレイバインディング

関数を特定の環境、つまりオブジェクトに強制的に適用したい場合は、 applycallbindを使用してthisバインドして実行することができます。各Functionオブジェクトにはapply()call()bind()メソッドがあり、特定のスコープ内で関数を呼び出すことができます。これは、関数本体でthisオブジェクトの値を設定して関数が実行されるスコープを拡張することと同じです。また、 bindを使用してthisをバインドする優先順位はapplycallよりも高いことに注意してください。つまり、 bindを使用してthisをバインドした後は、 applycallを使用してもthisのポイントを変更することはできません。

window.name = "A"; // ウィンドウオブジェクトにマウントされた名前
document.name = "B"; // ドキュメントオブジェクトにマウントされた名前
var s = { // オブジェクト s をカスタマイズする
 名前: 「C」
}

var ロールコール = {
 名前: 「先生」、
 sayName: 関数(){
 コンソールにログ出力します。
 }
}
rollCall.sayName(); // 先生

// 適用する
rollCall.sayName.apply(); // A // パラメータは渡されず、デフォルトのウィンドウがバインドされます
rollCall.sayName.apply(window); // A // ウィンドウオブジェクトをバインド rollCall.sayName.apply(document); // B // ドキュメントオブジェクトをバインド rollCall.sayName.apply(s); // C // カスタムオブジェクトをバインド // call
rollCall.sayName.call(); // A // パラメータは渡されず、デフォルトのウィンドウがバインドされます
rollCall.sayName.call(window); // A // window オブジェクトにバインド rollCall.sayName.call(document); // B // document オブジェクトにバインド rollCall.sayName.call(s); // C // カスタム オブジェクトにバインド // bind // 最後の () は実行用です rollCall.sayName.bind()(); //A // パラメータを渡さずに window にデフォルトでバインド
rollCall.sayName.bind(window)(); //A // ウィンドウオブジェクトをバインド rollCall.sayName.bind(document)(); //B // ドキュメントオブジェクトをバインド rollCall.sayName.bind(s)(); // C // カスタムオブジェクトをバインド

新しいバインディング

JavaScriptでは、 newコードの記述を簡素化し、オブジェクト インスタンスを一括で作成できる構文糖です。 newプロセスでは、実際に次の操作が実行されます。

空のプレーンJavaScriptオブジェクト (つまり{}を作成します。このオブジェクトを別のオブジェクトにリンクします (つまり、オブジェクトのコンストラクターを設定します)。手順1で作成したオブジェクトをthiscontextとして使用します。関数がオブジェクトを返さない場合は、手順1で作成されたオブジェクトが返されます。

関数_new(base,...args){
 var obj = {};
 obj.__proto__ = base.prototype;
 ベースを適用します(obj、args);
 obj を返します。
}

関数Funct(a) {
 これは、
}
var f1 = 新しいFunct(1);
コンソールログ(f1.a); // 1

var f2 = _new(関数、1);
コンソールログ(f2.a); // 1

矢印関数

矢印関数には別のthisありません。 this矢印関数の本体内で使用されると、 context環境内のthisが取得されます。矢印関数が呼び出されると、その関数自身のスコープ内ではthisは生成されません。スコープ チェーンの前のレベルからthisを継承するだけです。アロー関数には独自のthisポインタがないため、 applycallbindを使用するとパラメータを渡すことはできますが、アロー関数のthisポインタを動的に変更することはできません。また、アロー関数はコンストラクターとして使用できず、 newを使用してインスタンス化すると例外がスローされます。

ウィンドウ名 = 1;
var obj = {
 名前: 11,
 言う: function(){
 定数f1 = () => {
 this.name を返します。
 }
 console.log(f1()); // 11 // 直接の呼び出し元はwindowですが、矢印関数はthisをバインドしないため、コンテキスト内のthis、つまりobjオブジェクトが取得されます。const f2 = function(){
 this.name を返します。
 }
 console.log(f2()); // 1 // 直接の呼び出し元は通常の関数 window なので、this.name を返します。
 }
}

console.log(obj.say()); // 11 // 直接の呼び出し元はobjです。実行中の関数内のコンテキストのthisはobjオブジェクトです

関数s(){
 console.log(これを);
}

// ウィンドウ内で直接呼び出す // 非使用厳密
s(); // ウィンドウ // window.s() と同等、呼び出し元は window
// ウィンドウは Window のインスタンスです // window instanceof Window //true

// 新しいオブジェクト s1 を作成する
var s1 = {
 t1: function(){ // this が呼び出し元を指していることをテストする console.log(this); // s1
 s(); // ウィンドウ // この呼び出しはwindow.s()と同等ですが、呼び出し元はwindowです
 },
 t2: () => { // テスト矢印関数。これは呼び出し元を指していません。console.log(this);
 },
 t3: { // テストオブジェクト内のオブジェクト tt1: function() {
 console.log(これを);
 } 
 },
 t4: { // 矢印関数と非関数呼び出しをテストします。これは呼び出し元を指していません。tt1: () => {
 console.log(これを);
 } 
 },
 t5: function(){ // 関数呼び出しをテストする場合、矢印関数の this は前のオブジェクトの呼び出し元を指します。 return {
 tt1: () => {
 console.log(これを);
 }
 }
 }
}
s1.t1(); // s1 オブジェクト // ここでの呼び出し元は s1 なので、出力されるオブジェクトは s1 です
s1.t2(); // ウィンドウ
s1.t3.tt1(); // s1.t3 オブジェクト s1.t4.tt1(); // ウィンドウ
s1.t5().tt1(); // s1 オブジェクト

Js での this の詳細な理解に関するこの記事はこれで終わりです。Js での this の詳細な理解に関するより関連性の高いコンテンツについては、123WORDPRESS.COM で以前の記事を検索するか、次の関連記事を引き続き参照してください。今後も 123WORDPRESS.COM を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • JavaScript における実行環境とスコープチェーン
  • JavaScript スコープ、スコープ チェーン、クロージャの使用方法の詳細な説明
  • JavaScript におけるスコープチェーンの概念と使用法
  • JavaScript スコープの簡単な紹介
  • JavaScript の語彙スコープを詳しく見る
  • JavaScript スコープクローズの詳細な説明
  • JS におけるスコープと変数範囲の分析

<<:  MySQL innodb_autoinc_lock_mode について

>>:  CentOS の環境変数と設定ファイルの詳細な説明

推薦する

Docker コンテナにデプロイされた Django のタイムゾーンの問題

目次Django でのタイムゾーン設定USE_TZ=真USE_TZ=偽Linux コンテナでのタイム...

Javascriptジェネレータの紹介と使用

ジェネレータとは何ですか?ジェネレーターは関数内で実行されるコードです。値を返した後、一時停止し、呼...

HTML 特殊文字エンコーディング CSS3 コンテンツに関する簡単な説明:「私は特別なシンボルです」

プロジェクトで使用されている特殊文字とアイコンHTMLコードXML/HTML コードコンテンツをクリ...

MySQL binlog の使用方法の詳細な説明

binlog は、MySQL のすべての DML 操作を記録するバイナリ ログ ファイルです。 bi...

Ubuntu 18.04 Server に静的 IP を設定する方法

1. 背景Netplan は、Ubuntu システムのネットワーク設定を簡単に管理および構成できるよ...

Vueは買い物数量を変更できるショッピングカートを実装します

この記事では、Vueを使用してショッピングカートの数量を変更する方法を紹介します。具体的な内容は次の...

CSSは固定比率のブロックレベルコンテナを簡単に実装できる

H5 レイアウトを設計する場合、通常はバナーに遭遇することになります。例えば、2:1 で表示したい場...

MySQL における冗長インデックスと重複インデックスの違い

MySQL では、1 つの列に複数のインデックスを作成できます。意図的であるかどうかにかかわらず、M...

Angularルーティングアニメーションと高度なアニメーション機能の詳細な説明

目次1. ルーティングアニメーション2. グループクエリとスタガー1. ルーティングアニメーションル...

Node の SMS API で検証コード ログインを実装するためのサンプル コード

1. ノードサーバーのセットアップ + データベース接続ここでの操作は比較的簡単でわかりやすいです。...

mysql8.0.20 のデータディレクトリを移行する方法

mysql のデフォルトのストレージ ディレクトリは/var/lib/mysql/です。以下は、デフ...

システム エラー 1067 のため、MySQL 5.6 解凍バージョン サービスを開始できません

今日午後ずっと私を悩ませたバグを記録する半月前から始めましょう。それから.................

CentOS8 デプロイメント LNMP 環境で mysql8.0.29 をコンパイルしてインストールする方法の詳細なチュートリアル

1. 前提条件何度かインストールしているので、エラーについてはこれ以上説明しません。ちょっとわかりに...

Mysql 5.7.19 無料インストール版 (64 ビット) の設定方法に関する詳細なチュートリアル

公式サイトから mysql-5.7.19-winx64 をダウンロードします。これはシステムの 64...

nginx を介して方向プロキシを実装するプロセスの図

この記事は主に、nginx を介して方向プロキシを実装するプロセスを紹介します。この記事のサンプル ...