javascriptの変数のスコープは何ですか? 関数の外側とは対照的に、それらは同じスコープを内側に持っていますか? それとも問題ですか?また、変数がグローバルに定義されている場合、変数はどこに保存されますか?
27 に答える
TLDR
JavaScript には、レキシカル (静的とも呼ばれる) スコープとクロージャーがあります。これは、ソースコードを見ることで識別子の範囲を知ることができることを意味します。
4 つのスコープは次のとおりです。
- グローバル - すべてのものから見える
- 関数 - 関数 (およびそのサブ関数とブロック) 内で可視
- ブロック - ブロック (およびそのサブブロック) 内で可視
- モジュール - モジュール内で可視
グローバル スコープとモジュール スコープの特殊なケースを除き、変数はvar
(関数スコープ)、let
(ブロック スコープ)、およびconst
(ブロック スコープ) を使用して宣言されます。他のほとんどの形式の識別子宣言には、strict モードのブロック スコープがあります。
概要
スコープは、識別子が有効なコードベースの領域です。
レキシカル環境は、識別子名とそれらに関連付けられた値の間のマッピングです。
スコープは、レキシカル環境のリンクされたネストで形成され、ネスト内の各レベルは先祖実行コンテキストのレキシカル環境に対応します。
これらのリンクされたレキシカル環境は、スコープ「チェーン」を形成します。識別子の解決は、このチェーンに沿って一致する識別子を検索するプロセスです。
識別子の解決は、一方向 (外側) でのみ行われます。このように、外側の字句環境は内側の字句環境を「見る」ことができません。
JavaScriptで識別子のスコープを決定するには、次の 3 つの関連要因があります。
識別子を宣言できるいくつかの方法:
var
、let
およびconst
- 関数パラメーター
- キャッチ ブロック パラメーター
- 関数宣言
- 名前付き関数式
var
グローバル オブジェクトの暗黙的に定義されたプロパティ (つまり、非厳密モードでの欠落)import
ステートメントeval
位置識別子の一部を宣言できます。
- グローバル コンテキスト
- 関数本体
- 通常ブロック
- 制御構造の先頭 (例: loop、if、while など)
- 制御構造体
- モジュール
宣言のスタイル
変数
を使用して宣言された識別子は、グローバル コンテキストで直接宣言されている場合を除き、var
関数スコープを持ちます。この場合、それらはグローバル オブジェクトのプロパティとして追加され、グローバル スコープを持ちます。関数での使用には別の規則がありeval
ます。
let と const
グローバル コンテキストで直接宣言されている場合を除き、ブロック スコープlet
を使用して宣言された識別子は、グローバル スコープを持ちます。const
注: let
、const
およびvar
はすべて吊り上げられています。これは、定義の論理的な位置が、それらを囲むスコープ (ブロックまたは関数) の先頭であることを意味します。let
ただし、 andを使用して宣言された変数は、const
制御がソース コード内の宣言のポイントを通過するまで、読み取ったり、割り当てたりすることはできません。中間期間は、一時的なデッド ゾーンとして知られています。
function f() {
function g() {
console.log(x)
}
let x = 1
g()
}
f() // 1 because x is hoisted even though declared with `let`!
関数のパラメータ名
関数パラメーター名は、関数本体にスコープされます。これには少し複雑な点があることに注意してください。デフォルトの引数として宣言された関数は、関数の本体ではなく、パラメーター リストを閉じます。
関数宣言
関数宣言には、厳密モードではブロック スコープがあり、非厳密モードでは関数スコープがあります。注: 非厳密モードは、さまざまなブラウザーの風変わりな歴史的な実装に基づいた複雑な緊急ルールのセットです。
名前付き関数式
名前付き関数式は、それ自体にスコープが設定されます (たとえば、再帰の目的で)。
グローバル オブジェクトの暗黙的に定義されたプロパティ
非厳密モードでは、グローバル オブジェクトがスコープ チェーンの最上位に位置するため、グローバル オブジェクトで暗黙的に定義されたプロパティはグローバル スコープを持ちます。strict モードでは、これらは許可されていません。
評価
文字列ではeval
、使用して宣言された変数はvar
現在のスコープに配置されるか、eval
間接的に使用される場合はグローバル オブジェクトのプロパティとして配置されます。
例
名前x
、y
、およびz
は関数の外では意味がないため、次の例では ReferenceError がスローされますf
。
function f() {
var x = 1
let y = 1
const z = 1
}
console.log(typeof x) // undefined (because var has function scope!)
console.log(typeof y) // undefined (because the body of the function is a block)
console.log(typeof z) // undefined (because the body of the function is a block)
y
以下はとに対して ReferenceError をスローしますが、 に対してはスローしz
ませんx
。これは、 の可視性がx
ブロックによって制約されていないためです。if
、for
、 などの制御構造の本体を定義するブロックは、while
同様に動作します。
{
var x = 1
let y = 1
const z = 1
}
console.log(x) // 1
console.log(typeof y) // undefined because `y` has block scope
console.log(typeof z) // undefined because `z` has block scope
以下では、関数スコープがx
あるため、ループの外側に表示されます。var
for(var x = 0; x < 5; ++x) {}
console.log(x) // 5 (note this is outside the loop!)
var
...この動作のため、ループ内で使用して宣言された変数を閉じる際には注意が必要です。x
ここで宣言されているvariable のインスタンスは 1 つだけで、論理的にはループの外にあります。
次の例では5
、 が 5 回出力され、ループの外側で5
6 回出力されます。console.log
for(var x = 0; x < 5; ++x) {
setTimeout(() => console.log(x)) // closes over the `x` which is logically positioned at the top of the enclosing scope, above the loop
}
console.log(x) // note: visible outside the loop
はブロックスコープでundefined
あるため、次のように表示されます。x
コールバックは非同期で 1 つずつ実行されます。変数の新しい動作let
は、各無名関数が名前付きの異なる変数を閉じていることを意味しx
( で行われるのとは異なりvar
)、整数0
が4
出力されます。
for(let x = 0; x < 5; ++x) {
setTimeout(() => console.log(x)) // `let` declarations are re-declared on a per-iteration basis, so the closures capture different variables
}
console.log(typeof x) // undefined
ReferenceError
の可視性がx
ブロックによって制限されていないため、次は a をスローしません。ただし、undefined
変数が初期化されていないため(if
ステートメントのため)、出力されます。
if(false) {
var x = 1
}
console.log(x) // here, `x` has been declared, but not initialised
ループの先頭で宣言された変数for
usinglet
は、ループの本体にスコープされます。
for(let x = 0; x < 10; ++x) {}
console.log(typeof x) // undefined, because `x` is block-scoped
ReferenceError
の可視性がx
ブロックによって制限されているため、次の例では がスローされます。
if(false) {
let x = 1
}
console.log(typeof x) // undefined, because `x` is block-scoped
またはを使用して宣言された変数はvar
、すべてモジュールにスコープされます。let
const
// module1.js
var x = 0
export function f() {}
//module2.js
import f from 'module1.js'
console.log(x) // throws ReferenceError
var
グローバル コンテキスト内で使用して宣言された変数は、プロパティとしてグローバル オブジェクトに追加されるため、以下はグローバル オブジェクトのプロパティを宣言します。
var x = 1
console.log(window.hasOwnProperty('x')) // true
let
グローバル コンテキストでconst
は、グローバル オブジェクトにプロパティを追加しませんが、グローバル スコープは保持します。
let x = 1
console.log(window.hasOwnProperty('x')) // false
関数パラメーターは、関数本体で宣言されていると見なすことができます。
function f(x) {}
console.log(typeof x) // undefined, because `x` is scoped to the function
Catch ブロック パラメーターのスコープは、catch ブロック本体に限定されます。
try {} catch(e) {}
console.log(typeof e) // undefined, because `e` is scoped to the catch block
名前付き関数式は、式自体にのみスコープされます。
(function foo() { console.log(foo) })()
console.log(typeof foo) // undefined, because `foo` is scoped to its own expression
非厳密モードでは、グローバル オブジェクトで暗黙的に定義されたプロパティのスコープがグローバルになります。strict モードでは、エラーが発生します。
x = 1 // implicitly defined property on the global object (no "var"!)
console.log(x) // 1
console.log(window.hasOwnProperty('x')) // true
非厳密モードでは、関数宣言に関数スコープがあります。厳密モードでは、ブロック スコープがあります。
'use strict'
{
function foo() {}
}
console.log(typeof foo) // undefined, because `foo` is block-scoped
内部での仕組み
スコープは、識別子が有効なコードの字句領域として定義されます。
JavaScript では、すべての関数オブジェクトに、それが作成された実行コンテキスト(スタック フレーム)のレキシカル環境[[Environment]]
への参照である非表示の参照があります。
関数を呼び出すと、隠し[[Call]]
メソッドが呼び出されます。このメソッドは、新しい実行コンテキストを作成し、新しい実行コンテキストと関数オブジェクトのレキシカル環境との間のリンクを確立します。これは[[Environment]]
、関数オブジェクトの値を、新しい実行コンテキストのレキシカル環境の外部参照フィールドにコピーすることによって行われます。
新しい実行コンテキストと関数オブジェクトのレキシカル環境の間のこのリンクは、クロージャーと呼ばれることに注意してください。
したがって、JavaScript では、スコープは、外部参照によって「チェーン」にリンクされたレキシカル環境を介して実装されます。この字句環境の連鎖はスコープ連鎖と呼ばれ、識別子の解決は一致する識別子の連鎖を検索することによって行われます。
詳細をご覧ください。
Javascript は、スコープ チェーンを使用して、特定の関数のスコープを確立します。通常、1 つのグローバル スコープがあり、定義された各関数には独自のネストされたスコープがあります。別の関数内で定義された関数には、外側の関数にリンクされたローカル スコープがあります。スコープを定義するのは、常にソース内の位置です。
スコープ チェーン内の要素は、基本的に、親スコープへのポインターを持つ Map です。
変数を解決するとき、javascript は最も内側のスコープから開始し、外側に向かって検索します。
グローバルに宣言された変数には、グローバル スコープがあります。関数内で宣言された変数はその関数にスコープされ、同じ名前のグローバル変数を隠します。
(実際の JavaScript プログラマーが他の回答で指摘できる微妙な点がたくさんあると確信しています。特に、いつでも正確に何を意味するかについてこのページに出くわしました。うまくいけば、このより紹介的なリンクで十分に始めることができます。 .)this
古い学校の JavaScript
従来、JavaScript には実際には 2 種類のスコープしかありません。
- グローバル スコープ: 変数は、アプリケーションの開始時から、アプリケーション全体で認識されます(*)
- 機能範囲: 変数は、関数の先頭から、変数が宣言されている関数内で既知です(*)
違いを説明する他の多くの回答が既にあるため、これについては詳しく説明しません。
最新の JavaScript
最新の JavaScript 仕様では、3 番目のスコープも許可されるようになりました。
- ブロック スコープ: 識別子は、宣言されているスコープの先頭から「認識」されますが、宣言の行の後まで、割り当てまたは逆参照 (読み取り) することはできません。この一時的な期間は「一時的なデッド ゾーン」と呼ばれます。
ブロック スコープ変数を作成するにはどうすればよいですか?
伝統的に、次のように変数を作成します。
var myVariable = "Some text";
ブロック スコープ変数は次のように作成されます。
let myVariable = "Some text";
では、機能範囲とブロック範囲の違いは何ですか?
機能スコープとブロック スコープの違いを理解するには、次のコードを検討してください。
// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here
function loop(arr) {
// i IS known here, but undefined
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
for( var i = 0; i < arr.length; i++ ) {
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
};
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
for( let j = 0; j < arr.length; j++ ) {
// i IS known here, and has a value
// j IS known here, and has a value
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
};
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
}
loop([1,2,3,4]);
for( var k = 0; k < arr.length; k++ ) {
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here
};
for( let l = 0; l < arr.length; l++ ) {
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS known here, and has a value
};
loop([1,2,3,4]);
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here
ここで、変数j
は最初の for ループでのみ認識され、前後では認識されないことがわかります。それでも、変数i
は関数全体で既知です。
また、ブロック スコープの変数はホイストされていないため、宣言される前にはわからないことを考慮してください。また、同じブロック内で同じブロック スコープの変数を再宣言することもできません。これにより、ブロックスコープの変数は、グローバルまたは関数スコープの変数よりもエラーが発生しにくくなります。グローバルまたは関数スコープの変数は、ホイストされ、複数の宣言の場合にエラーを生成しません。
現在、ブロック スコープ変数を使用しても安全ですか?
現在安全に使用できるかどうかは、環境によって異なります。
サーバー側の JavaScript コード ( Node.js
let
) を作成している場合は、このステートメントを安全に使用できます。クライアント側の JavaScript コードを作成していて、ブラウザー ベースのトランスパイラー ( Traceurやbabel-standaloneなど) を使用している場合は、ステートメントを安全に使用できますが
let
、コードはパフォーマンスに関して最適化されていない可能性があります。クライアント側の JavaScript コードを作成していて、Node ベースのトランスパイラー ( traceur シェル スクリプトやBabel
let
など) を使用している場合は、ステートメントを安全に使用できます。また、ブラウザはトランスパイルされたコードしか認識しないため、パフォーマンスの欠点は限定されます。クライアント側の JavaScript コードを作成していて、トランスパイラーを使用していない場合は、ブラウザーのサポートを考慮する必要があります。
以下は、まったくサポートしていないブラウザーの一部です
let
。- インターネットエクスプローラー10以下
- Firefox 43以下
- Safari9以下
- Android ブラウザ 4以下
- Opera 27以下
- 40丁目以下
- Opera MiniとBlackberry ブラウザの任意のバージョン
ブラウザのサポートを追跡する方法
let
この回答を読んだ時点でどのブラウザーがステートメントをサポートしているかの最新の概要については、このCan I Use
ページを参照してください。
(*) グローバルおよび関数スコープの変数は、JavaScript 変数が巻き上げられるため、宣言される前に初期化して使用できます。これは、宣言が常にスコープの最上位にあることを意味します。
次に例を示します。
<script>
var globalVariable = 7; //==window.globalVariable
function aGlobal( param ) { //==window.aGlobal();
//param is only accessible in this function
var scopedToFunction = {
//can't be accessed outside of this function
nested : 3 //accessible by: scopedToFunction.nested
};
anotherGlobal = {
//global because there's no `var`
};
}
</script>
クロージャーと、それらを使用してプライベート メンバーを作成する方法を調査する必要があります。
重要なのは、私が理解しているように、Javascriptには関数レベルのスコープとより一般的なCブロックのスコープがあるということです。
「Javascript 1.7」(Mozilla の Javascript への拡張機能) では、let
ステートメントでブロックスコープ変数を宣言することもできます:
var a = 4;
let (a = 3) {
alert(a); // 3
}
alert(a); // 4
1) グローバル スコープ、関数スコープ、および with スコープと catch スコープがあります。一般に、変数には「ブロック」レベルのスコープはありません。 with ステートメントと catch ステートメントは、それらのブロックに名前を追加します。
2) スコープは、グローバル スコープまで関数によってネストされます。
3) プロパティは、プロトタイプ チェーンを経由して解決されます。with ステートメントは、オブジェクトのプロパティ名を with ブロックで定義されたレキシカル スコープに取り込みます。
編集: ECMAAScript 6 (ハーモニー) は let をサポートするように仕様化されており、クロムが「ハーモニー」フラグを許可していることを知っているので、おそらくそれをサポートしています..
Let はブロック レベルのスコープのサポートになりますが、それを実現するにはキーワードを使用する必要があります。
編集: コメント内の with ステートメントと catch ステートメントに対するベンジャミンの指摘に基づいて、投稿を編集し、さらに追加しました。with ステートメントと catch ステートメントの両方が、それぞれのブロックに変数を導入します。これがブロック スコープです。これらの変数は、渡されたオブジェクトのプロパティにエイリアスされます。
//chrome (v8)
var a = { 'test1':'test1val' }
test1 // error not defined
with (a) { var test1 = 'replaced' }
test1 // undefined
a // a.test1 = 'replaced'
編集:明確な例:
test1 のスコープは with ブロックですが、エイリアスは a.test1 です。'Var test1' は、新しい変数 test1 を上位レキシカル コンテキスト (関数またはグローバル) に作成します。ただし、それが a のプロパティである場合を除きます。
うわぁ!「with」の使用には注意してください。変数が関数内で既に定義されている場合、var が noop であるのと同様に、オブジェクトからインポートされた名前に関しても noop です。すでに定義されている名前に少し注意を払うと、これがより安全になります。このため、私は個人的に with を使用することはありません。
JavaScript を初めて使用する多くの人は、この言語ではデフォルトで継承が使用可能であり、これまでのところ関数スコープが唯一のスコープであることを理解するのに苦労していることに気付きました。JSPretty と呼ばれる、昨年末に作成したビューティファイアーの拡張機能を提供しました。この機能は、コード内の関数スコープを色付けし、そのスコープで宣言されたすべての変数に常に色を関連付けます。あるスコープの色を持つ変数が別のスコープで使用されると、閉鎖が視覚的に示されます。
次の場所で機能を試してください。
次の場所でデモをご覧ください。
次の場所でコードを表示します。
- http://prettydiff.com/lib/jspretty.js
- https://github.com/austincheney/Pretty-Diff/blob/master/lib/jspretty.js
現在、この機能は 16 のネストされた関数の深さをサポートしていますが、現在グローバル変数を色付けしていません。
グローバル スコープ :
グローバル変数は、グローバル スター (ジャッキー チェン、ネルソン マンデラ) とまったく同じです。アプリケーションのどの部分からでも、それらにアクセス (値を取得または設定) できます。グローバル関数は、グローバル イベント (新年、クリスマス) のようなものです。アプリケーションのどの部分からでも実行 (呼び出し) できます。
//global variable
var a = 2;
//global function
function b(){
console.log(a); //access global variable
}
ローカル スコープ :
あなたがアメリカにいるなら、悪名高い有名人のキム・カーダシアンを知っているかもしれません (彼女はどうにかしてタブロイド紙を作ることに成功しています)。しかし、米国外の人々は彼女を認識しません. 彼女は自分のテリトリーに縛られた地元のスターです。
ローカル変数はローカル スターのようなものです。スコープ内でのみアクセス (値の取得または設定) できます。ローカル関数はローカル イベントのようなものです。そのスコープ内でのみ実行 (祝う) ことができます。スコープ外からアクセスしようとすると参照エラーになる
function b(){
var d = 21; //local variable
console.log(d);
function dog(){ console.log(a); }
dog(); //execute local function
}
console.log(d); //ReferenceError: dddddd is not defined
コードを実行します。これがスコーピングについてのアイデアを与えることを願っています
Name = 'global data';
document.Name = 'current document data';
(function(window,document){
var Name = 'local data';
var myObj = {
Name: 'object data',
f: function(){
alert(this.Name);
}
};
myObj.newFun = function(){
alert(this.Name);
}
function testFun(){
alert("Window Scope : " + window.Name +
"\nLocal Scope : " + Name +
"\nObject Scope : " + this.Name +
"\nCurrent document Scope : " + document.Name
);
}
testFun.call(myObj);
})(window,document);
JavaScript には 2 種類のスコープしかありません。
- グローバル スコープ: グローバルは、ウィンドウ レベルのスコープに他なりません。ここでは、アプリケーション全体に存在する変数です。
- Functional Scope
var
:キーワードを使用して関数内で宣言された変数には、機能スコープがあります。
関数が呼び出されるたびに、変数スコープ オブジェクトが作成され (スコープ チェーンに含まれます)、その後に JavaScript の変数が続きます。
a = "global";
function outer(){
b = "local";
console.log(a+b); //"globallocal"
}
outer();
スコープチェーン -->
- ウィンドウ レベル -関数はスコープ チェーンのトップ レベルにあります
a
。outer
- 外部関数が new を呼び出したとき
variable scope object
(およびスコープ チェーンに含まれたとき) に変数が追加されましb
た。
変数a
が必要な場合、最初に最も近い変数スコープを検索し、そこに変数がない場合は、変数スコープ チェーンの次のオブジェクトに移動します。この場合はウィンドウ レベルです。
インライン ハンドラ
フロントエンド コーダーが頻繁に遭遇する、まだ説明されていない非常に一般的な問題は、HTML 内のインライン イベント ハンドラーに表示されるスコープです。
<button onclick="foo()"></button>
on*
属性が参照できる変数のスコープは、次のいずれかでなければなりません。
- グローバル (動作中のインライン ハンドラーは、ほとんど常にグローバル変数を参照します)
- ドキュメントのプロパティ (たとえば、
querySelector
スタンドアロンの変数が指すようにdocument.querySelector
;まれ) - ハンドラーがアタッチされている要素のプロパティ (上記のように; まれ)
そうしないと、ハンドラーが呼び出されたときに ReferenceError が発生します。たとえば、インライン ハンドラがor内 で定義されている関数を参照する場合、インライン ハンドラはグローバル スコープ内の変数のみを参照でき、関数はグローバルではないため、参照は失敗します。window.onload
$(function() {
window.addEventListener('DOMContentLoaded', () => {
function foo() {
console.log('foo running');
}
});
<button onclick="foo()">click</button>
インライン ハンドラーは2 つのブロック(1 つは、もう 1 つは要素)内で呼び出されるため、ハンドラーがアタッチされる要素の および プロパティのプロパティはdocument
、インライン ハンドラー内のスタンドアロン変数として参照される場合もあります。これらのハンドラー内の変数のスコープ チェーンは非常に直感的ではなく、機能するイベント ハンドラーにはおそらく関数がグローバルである必要があります (不要なグローバル汚染はおそらく回避する必要があります)。with
document
インライン ハンドラー内のスコープ チェーンは非常に奇妙であり、インライン ハンドラーが動作するにはグローバルな汚染が必要であり、インライン ハンドラーは引数を渡すときに醜い文字列エスケープを必要とする場合があるため、おそらくそれらを回避する方が簡単です。addEventListener
代わりに、 HTML マークアップを使用するのではなく、Javascript を使用して ( のように) イベント ハンドラーをアタッチします。
function foo() {
console.log('foo running');
}
document.querySelector('.my-button').addEventListener('click', foo);
<button class="my-button">click</button>
モジュール ( <script type="module">
)
別の注意として、最上位で実行される通常の<script>
タグとは異なり、ES6 モジュール内のコードは独自のプライベート スコープで実行されます。通常のタグの先頭で定義された変数はグローバルであるため、次のように他のタグで<script>
参照できます。<script>
<script>
const foo = 'foo';
</script>
<script>
console.log(foo);
</script>
しかし、ES6 モジュールの最上位はグローバルではありません。export
ES6 モジュールの先頭で宣言された変数は、変数が明示的に編集されるか、グローバル オブジェクトのプロパティに割り当てられない限り、そのモジュール内でのみ表示されます。
<script type="module">
const foo = 'foo';
</script>
<script>
// Can't access foo here, because the other script is a module
console.log(typeof foo);
</script>
ES6モジュールのトップレベルは、通常のトップレベルのIIFEの内部と似ています<script>
。モジュールは、グローバルな変数を参照できます。モジュールが明示的に設計されていない限り、モジュール内の何も参照できません。
JavaScript スコープには、ほぼ 2 種類しかありません。
- 各 var 宣言のスコープは、最もすぐ外側の関数に関連付けられています
- var 宣言を囲む関数がない場合、それはグローバル スコープです。
したがって、関数以外のブロックは新しいスコープを作成しません。これは、for ループが外側のスコープ変数を上書きする理由を説明しています。
var i = 10, v = 10;
for (var i = 0; i < 5; i++) { var v = 5; }
console.log(i, v);
// output 5 5
代わりに関数を使用する:
var i = 10, v = 10;
$.each([0, 1, 2, 3, 4], function(i) { var v = 5; });
console.log(i,v);
// output 10 10
最初の例では、ブロック スコープがなかったため、最初に宣言された変数が上書きされました。2 番目の例では、関数のために新しいスコープがあったため、最初に宣言された変数は SHADOWED であり、上書きされませんでした。
JavaScript のスコープに関して知っておく必要があるのは、ほとんどこれだけです。ただし、次の点を除きます。
- try/catch は、例外変数自体に対してのみ新しいスコープを導入します。他の変数には新しいスコープはありません
- with-clause は明らかに別の例外ですが、 with-clause の使用は強くお勧めしません ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with )
このように、常に直感的であるとは限りませんが、JavaScript のスコープは実際には非常に単純であることがわかります。次の点に注意してください。
- var 宣言はスコープの先頭に引き上げられます。これは、var 宣言がどこで発生しても、コンパイラにとっては、var 自体が先頭にあるかのように見えることを意味します。
- 同じスコープ内の複数の var 宣言が結合されています
したがって、このコード:
var i = 1;
function abc() {
i = 2;
var i = 3;
}
console.log(i); // outputs 1
次と同等です。
var i = 1;
function abc() {
var i; // var declaration moved to the top of the scope
i = 2;
i = 3; // the assignment stays where it is
}
console.log(i);
これは直感に反するように思えるかもしれませんが、命令型言語の設計者の観点からは理にかなっています。
JS には関数スコープしかありません。スコープをブロックしないでください! 巻き上げている様子も見られます。
var global_variable = "global_variable";
var hoisting_variable = "global_hoist";
// Global variables printed
console.log("global_scope: - global_variable: " + global_variable);
console.log("global_scope: - hoisting_variable: " + hoisting_variable);
if (true) {
// The variable block will be global, on true condition.
var block = "block";
}
console.log("global_scope: - block: " + block);
function local_function() {
var local_variable = "local_variable";
console.log("local_scope: - local_variable: " + local_variable);
console.log("local_scope: - global_variable: " + global_variable);
console.log("local_scope: - block: " + block);
// The hoisting_variable is undefined at the moment.
console.log("local_scope: - hoisting_variable: " + hoisting_variable);
var hoisting_variable = "local_hoist";
// The hoisting_variable is now set as a local one.
console.log("local_scope: - hoisting_variable: " + hoisting_variable);
}
local_function();
// No variable in a separate function is visible into the global scope.
console.log("global_scope: - local_variable: " + local_variable);
私は受け入れられた答えが本当に好きですが、これを追加したいと思います:
Scope は、宣言されたすべての識別子 (変数) のルックアップ リストを収集して維持し、現在実行中のコードからこれらの識別子にアクセスする方法について、一連の厳密な規則を適用します。
スコープは、識別子名で変数を検索するための一連のルールです。
- 直接のスコープで変数が見つからない場合、Engine は次の外側のスコープを参照し、変数が見つかるまで、または最も外側の (別名、グローバル) スコープに到達するまで続行します。
- 変数 (識別子) を検索できる場所と方法を決定する一連の規則です。このルックアップは、LHS (左側) 参照である変数への割り当てを目的とするか、RHS (右側) 参照であるその値を取得することを目的とする場合があります。 .
- LHS 参照は代入操作の結果です。スコープ関連の代入は、= 演算子を使用するか、引数を関数パラメーターに渡す (代入する) ことによって行うことができます。
- JavaScript エンジンは、実行前にまずコードをコンパイルします。その際、var a = 2; のようにステートメントを分割します。2 つの別々のステップに分けます。まず、var a でそのスコープで宣言します。これは、コード実行前の最初に実行されます。2番目。後で、a = 2 で変数 (LHS 参照) を検索し、見つかった場合はそれに割り当てます。
- LHS と RHS の両方の参照ルックアップは、現在実行中のスコープから開始し、必要に応じて (つまり、そこで探しているものが見つからない場合)、ネストされたスコープを 1 つのスコープ (フロア) 一度に識別子を探し、グローバル (最上階) に到達して停止し、それを見つけるか、見つけられないかのいずれかになります。RHS 参照が満たされていない場合、ReferenceError がスローされます。満たされていない LHS 参照は、その名前の自動的に暗黙的に作成されたグローバル (Strict モードでない場合)、または ReferenceError (Strict モードの場合) になります。
- スコープは、それぞれがコンテナーまたはバケットとして機能する一連の「バブル」で構成され、その中で識別子 (変数、関数) が宣言されます。これらのバブルは互いにきれいにネストされ、このネストは作成時に定義されます。
JavaScript には 2 種類のスコープがあります。
グローバル スコープ: グローバル スコープで宣言された変数は、プログラム内のどこでも非常にスムーズに使用できます。例えば:
var carName = " BMW"; // code here can use carName function myFunction() { // code here can use carName }
Functional スコープまたは Local スコープ: このスコープで宣言された変数は、それ自体の関数でのみ使用できます。例えば:
// code here can not use carName function myFunction() { var carName = "BMW"; // code here can use carName }