序文
プログラミング言語を学ぶには、範囲を認識する必要があります。スコープは識別子の定義とライフサイクルに影響を与えるためです。納得がいかないので、あまり話せないので、一緒に勉強しましょう〜
プログラミングにおけるスコープの役割
スコープを「オブジェクト」と見なします。このオブジェクトには独自の気性があります(ps:気性がルールです)が、コード化された方法でオブジェクトにアクセスすることはできません。JavaScriptエンジン内に存在し、それに応じて管理するために使用されます。現在のスコープでエンジンがどのように機能するか。スコープを深く理解することで、有効な識別子の範囲をより適切に識別し、エンコードプロセス中に識別子を検索できます。
スコープの分類
JavaScriptの言語では、字句スコープが使用されます。つまり、コードを記述するときに変数とブロックレベルのスコープを記述します。(追記:静的スコープとして理解できます)。
静的スコープの反対は動的スコープです。もちろん、JavaScriptには動的スコープを使用する特別なケースもいくつかあります。eval(...)やwithなど。(ps:パフォーマンスの問題を考慮すると、通常はお勧めしません)。
変数をコーディングおよび定義するとき、これらの変数のスコープはすでに決定されています。これらの変数の有効なスコープを分析する方法では、最初にJavaScriptスコープの分類を理解する必要があります。JavaScriptのスコープは次のとおりです。
- グローバルスコープ
- 関数スコープ
- ブロックスコープ
グローバルスコープ
グローバルスコープは、変数の有効スコープにコード内の任意の場所からアクセスできるため、理解できます。
-
ウィンドウはグローバルオブジェクトであるため、ウィンドウオブジェクトのプロパティにはグローバルスコープがあります
次に、コードの最外層の関数によって宣言された関数、最外の関数の外側のvarによって宣言された変数、およびキーワード宣言なしの直接LHSクエリ割り当て操作がすべてウィンドウオブジェクトにマウントされます。次のコードに示すように:
function foo(){
innerValueB=4 //会先在window对象上创建一个innerValueB属性再赋值
console.log('foo')
}
foo();
var outValueB=3;
console.log('window:',this,this.foo,this.outValueB,this.innerValueB)
-
letおよびconst宣言による関数の最外層
letで宣言された変数またはconstで宣言された定数は、ウィンドウオブジェクトにバインドされませんが、関数の最外部レベルで宣言すると、それらもグローバルスコープに属します。
let a=3;
console.log(this.a,this) //undefined window
上記の方法で変数を定義および宣言するために、それらはすべてグローバル変数です。ブラウザエンジンによってリサイクルされません(ps:手動でnullに設定されていない限り)。次に、プロジェクト内のグローバル変数の増加に伴い、変数名の競合が発生するだけでなく、メモリリークが発生する可能性があります。
表示:グローバル変数は、コーディングプロセスでできるだけ使用しないでください。
関数スコープ
関数スコープは関数内で宣言された変数であり(ps:ローカル変数と呼ぶことができます)、変数の有効スコープは通常、関数内でアクセスできます。もちろん、特別な場合もあります(ps:クロージャを除く)。
関数スコープは、関数宣言と関数式によって自動的に生成されます。次に例を示します。
function foo1(){...} //通过function声明 foo1
(function foo2(){...})(); //立即执行函数 foo2
関数foo1はグローバルスコープで宣言されているため、foo1には任意の場所からアクセスできますが、その内部変数には{...}でのみアクセスでき、関数foo2はfoo2に{...}でのみアクセスできることを意味します。その他場所にアクセスできず、その内部変数には{...}でのみアクセスできます。
関数スコープは、関数が実行されているかどうかに関係なく、バブルと見なすことができます。関数の外部から内部変数にアクセスすることはできません(ps:クロージャを除く)。たとえば、次のコードはレポートしますReferenceError: a is not defined
function bar(){
var a=2;
}
bar(); //无论bar有没执行,a变量都无法在外部获取
console.log(a)
入れ子関数が検出されると、エンジンは次のように、内側から外側にレイヤーごとに検索します。
function bar(){
var a=2; //局部变量 a
function next(){
var b=a;
console.log(b);
}
}
bar(); //执行bar最终输出b的值为2
関数スコープは内部実装を非表示にします。関数が実行されると、外部スコープを汚染することなく、内部変数のライフサイクルも終了します(ps:クロージャを除く)。
ブロックスコープ
ブロックレベルのスコープにはES5にはそのような概念はありませんが、ES6の一部の構文はjsに色を追加します。ブロックレベルのスコープもバブルと見なすことができ、宣言された変数はバブルを離れることができません。ブロックレベルのスコープを使用すると、変数の有効範囲に{...}の範囲内でアクセスできます。
letで宣言された変数と、constで宣言された定数は、次のようにブロックレベルのスコープを自動的にハイジャックします。
if(true){
let a=3
}
console.log(a) //ReferenceError: a is not defined
for(let b=0;b<3;b++){
console.log(b)
}
console.log(b) //ReferenceError: b is not defined
上記のコードはlet
キーワードをvar
キーワードに変換するため、出力状況は別の状況です。その理由は、letで宣言された変数がブロックレベルのスコープを自動的に生成するためです。ifステートメントまたはforループが実行されると、変数も破棄されます。 、ライフサイクルの終わり。
上記はJavaScriptのスコープですが、jsエンジンはどのようにして自由変数のスコープクエリを実行しますか?
**スコープクエリについては、クエリを裏返しにすることについて説明します。ただし、前提条件に注意してください。定義識別子が作成されるとき、クエリはスコープの内側から外側に向かっています。****
例えば:
var a = 100
function F1() {
var b = 200
function F2() {
var c = 300
console.log(a) // a的查询顺序F2=>F1=>window
console.log(b) // a的查询顺序F2=>F1
console.log(c) // c的查询顺序F2=>F1=>window,没找到报(ReferenceError)
}
F2()
}
F1()
次のコードfnはグローバルスコープで定義されているため、xの値は10です。** xの値は、fnが呼び出される場所とは関係がなく、fnが定義および作成される場所に関連しています**。
var x = 10
function fn() {
console.log(x) //x的查询顺序fn=>window
}
function show(f) {
var x = 20;
(function() {
f() //10,而不是20
})()
}
show(fn)
補足:スコープ検索は、内側から外側にクエリを実行することです。そのクエリルールは、定義の作成時に指定されることに注意してください。これにより、jsが静的スコープであるという概念も検証されます。それと実行コンテキストは2つの異なる概念です。(追記:混同しないでください)
実行コンテキストは、上記の実行を決定するために実行が呼び出される場所に基づいており、動的です。覚えておいてください!!
総括する
JavaScriptのスコープをもう一度学んだ後、私は知識の新しい理解を得ました。記録と共有は素晴らしいプロセスです。新しい洞察や質問がある場合は、コメントセクションでお会いしましょう。次回はスコープクロージャーのような特別な構造を共有します~~
さらに、暇なときに一連のフロントエンドの面接の質問をしました。困っている友達は、下の青い文字をクリックして読むことができます。