JavaScriptの変数のスコープは何ですか?関数の外側ではなく、内側に同じスコープがありますか?それとも問題ですか?また、グローバルに定義されている場合、変数はどこに保存されますか?
JavaScriptの変数のスコープは何ですか?関数の外側ではなく、内側に同じスコープがありますか?それとも問題ですか?また、グローバルに定義されている場合、変数はどこに保存されますか?
回答:
JavaScriptには字句(静的とも呼ばれます)のスコープとクロージャーがあります。これは、ソースコードを見ることで識別子のスコープを知ることができることを意味します。
4つのスコープは次のとおりです。
グローバルスコープとモジュールスコープの特別な場合を除いて、変数はvar
(関数スコープ)、let
(ブロックスコープ)、const
(ブロックスコープ)を使用して宣言されます。他のほとんどの形式の識別子宣言は、strictモードでブロックスコープを持っています。
スコープは、識別子が有効であるコードベースの領域です。
字句環境は、識別子名とそれに関連付けられた値の間のマッピングです。
スコープは、リンクされた字句環境のネストから形成され、ネストの各レベルは、祖先実行コンテキストの字句環境に対応しています。
これらのリンクされた字句環境は、スコープ「チェーン」を形成します。識別子の解決は、一致する識別子をこのチェーンに沿って検索するプロセスです。
識別子の解決は、一方向にのみ行われます。つまり、外側です。このようにして、外部の字句環境は内部の字句環境を「見る」ことができません。
JavaScriptで識別子のスコープを決定するには、3つの適切な要素があります。
識別子を宣言する方法のいくつか:
var
、let
およびconst
var
、非厳密モードでは欠落)import
ステートメントeval
一部の場所識別子は宣言できます。
を使用しvar
て宣言された識別子は、グローバルコンテキストで直接宣言されている場合を除いて、関数スコープを持っています。この場合、グローバルオブジェクトのプロパティとして追加され、グローバルスコープがあります。での使用には別のルールがありますeval
関数でのます。
を使用let
して宣言され、ブロックスコープを持つ識別子const
グローバルコンテキストで直接宣言されている場合を除い。この場合、グローバルスコープがあります。
注:let
、const
およびvar
すべての掲揚されています。これは、それらの定義の論理的な位置が、それらを包含するスコープ(ブロックまたは関数)の最上位であることを意味します。ただし、変数はuseing宣言let
及びconst
読み出しまたは制御は、ソースコード内の宣言のポイントを通過するまでに割り当てることはできません。暫定期間は、一時的な不感帯として知られています。
function f() {
function g() {
console.log(x)
}
let x = 1
g()
}
f() // 1 because x is hoisted even though declared with `let`!
関数パラメーター名のスコープは関数本体です。これには少し複雑なことに注意してください。デフォルトの引数として宣言された関数は、パラメーターリストを閉じますた関数は、関数の本体ではなく、ます。
関数宣言には、strictモードではブロックスコープ、non-strictモードでは関数スコープがあります。注:非厳格モードは、さまざまなブラウザーの風変わりな履歴実装に基づく複雑な緊急ルールのセットです。
名前付き関数式は、それ自体にスコープが設定されます(たとえば、再帰の目的で)。
非厳密モードでは、グローバルオブジェクトがスコープチェーンの最上位にあるため、グローバルオブジェクトの暗黙的に定義されたプロパティにはグローバルスコープがあります。厳密モードでは、これらは許可されません。
ではeval
、文字列、使用して宣言された変数は、var
現在のスコープに配置された、または、もしされますeval
グローバルオブジェクトのプロパティとして、間接的に使用されます。
以下は、名前ためにReferenceErrorがスローされますx
、y
およびz
機能のは意味の外側を持っていません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
and z
に対してReferenceErrorをスローしますが、に対しては表示されません。これは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
、ループで使用して宣言された変数を閉じる場合は注意が必要です。変数のインスタンスは1つだけですx
宣言されており、論理的にループの外側に配置されています。
次の例では5
、5回印刷5
しconsole.log
、ループの外側で6回印刷します。
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
ブロックによって制約されていないため、をスローしません。ただし、undefined
変数が初期化されていないために出力されます(if
ステートメントのため)。
if(false) {
var x = 1
}
console.log(x) // here, `x` has been declared, but not initialised
for
を使用してループの先頭で宣言された変数は、ループlet
の本体をスコープとしています。
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-block本文にスコープされます。
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
非厳密モードでは、グローバルオブジェクトの暗黙的に定義されたプロパティはグローバルにスコープされます。厳密モードでは、エラーが発生します。
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つのグローバルスコープがあり、定義された各関数には独自のネストされたスコープがあります。別の関数内で定義された関数には、外部関数にリンクされたローカルスコープがあります。スコープを定義するのは、常にソース内の位置です。
スコープチェーンの要素は、基本的には親スコープへのポインターを持つマップです。
変数を解決するとき、JavaScriptは最も内側のスコープから始まり、外側を検索します。
グローバルに宣言された変数には、グローバルスコープがあります。関数内で宣言された変数は、その関数をスコープとし、同じ名前のグローバル変数をシャドウします。
(本当のJavaScriptプログラマーが他の答えで指摘できる多くの微妙な点があると確信しています。特に、いつでも正確に何が意味されるかについてこのページに出くわしましthis
た。うまくいけば、このより多くの紹介リンクで十分に始められます。 。)
従来、JavaScriptには実際には2種類のスコープしかありません。
違いを説明する他の多くの回答がすでにあるため、これについては詳しく説明しません。
最新のJavaScriptのスペックは、今も、第三のスコープを許可します:
従来は、次のように変数を作成します。
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コードを記述していて、ノードベースのトランスパイラー(traceurシェルスクリプトやBabelなど)を使用している場合は、let
ステートメントを安全に使用できます。また、ブラウザーはトランスパイルされたコードのみを認識するため、パフォーマンスの低下は制限されます。
クライアント側のJavaScriptコードを記述していて、トランスパイラーを使用しない場合は、ブラウザーのサポートを検討する必要があります。
以下は、まったくサポートlet
していないブラウザです。
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
let
。
JavaScriptでスコープするというアイデアは、もともとブレンダンアイヒによって設計されたときに、HyperCardスクリプト言語HyperTalkから生まれました。
この言語では、表示はインデックスカードのスタックと同様に行われました。背景と呼ばれるマスターカードがありました。それは透明で、一番下のカードとして見ることができます。この基本カードのコンテンツは、その上に配置されたカードと共有されました。一番上に置かれた各カードには、前のカードよりも優先される独自のコンテンツがありましたが、必要に応じて以前のカードにアクセスできました。
これがまさにJavaScriptスコープシステムの設計方法です。名前が違うだけです。JavaScriptのカードは、実行コンテキストECMAとして知られていますます。これらの各コンテキストには、3つの主要な部分があります。変数環境、字句環境、およびthisバインディング。カードのリファレンスに戻ると、字句環境には、スタックの下位にある以前のカードのすべてのコンテンツが含まれています。現在のコンテキストはスタックの最上位にあり、そこで宣言されたコンテンツはすべて変数環境に格納されます。名前の衝突が発生した場合は、変数環境が優先されます。
thisバインディングは、それを含むオブジェクトを指します。含まれているオブジェクトが存在する可能性のある宣言された関数window
やコンストラクター関数など、含まれているオブジェクトを変更せずにスコープまたは実行コンテキストが変更される場合があります。
これらの実行コンテキストは、コントロールが転送されるたびに作成されます。コードが実行を開始すると制御が移り、これは主に関数の実行から行われます。
これが技術的な説明です。実際には、JavaScriptでは
これをこのページの前の例(5.「クロージャー」)の1つに適用すると、実行コンテキストのスタックをたどることができます。この例では、スタックに3つのコンテキストがあります。それらは、外側のコンテキスト、var sixによって呼び出される即時に呼び出される関数のコンテキスト、およびvar sixの即時に呼び出される関数内の返される関数のコンテキストによって定義されます。
i)外部コンテキスト。それはa = 1の変数環境を持っています
ii)IIFEコンテキスト、それはa = 1の字句環境を持っていますが、スタックで優先されるa = 6の変数環境
iii)返された関数コンテキスト、それは字句を持っていますa = 6の環境。これは、呼び出されたときにアラートで参照される値です。
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'は、それがaのプロパティでない限り、上位の字句コンテキスト(関数またはグローバル)に新しい変数test1を作成します。
うわぁ!「with」の使用には注意してください。変数が関数ですでに定義されている場合、varはnoopであるのと同様に、オブジェクトからインポートされた名前に関してもnoopです!すでに定義されている名前に少し手を加えると、これははるかに安全になります。このため個人的には使用しません。
with
ステートメントはブロックスコープの形式ですが、catch
句はより一般的な形式です(楽しい事実、v8はで実装さcatch
れますwith
)-JavaScript自体のブロックスコープの唯一の形式です(つまり、関数、グローバル、try / catchです)。 、およびその派生物)、ただし、ホスト環境ではスコープの概念が異なります。たとえば、ブラウザーのインラインイベントやNodeJSのvmモジュールなどです。
JavaScriptを初めて使う人の多くは、言語で継承がデフォルトで利用可能であり、関数スコープがこれまでのところ唯一のスコープであることを理解するのに苦労していることがわかりました。昨年末に書いた美容師の拡張機能としてJSPrettyを提供しました。機能はコード内の関数スコープを色分けし、常にそのスコープで宣言されたすべての変数に色を関連付けます。あるスコープの色を持つ変数が別のスコープで使用されると、クロージャが視覚的に示されます。
この機能を試してください:
デモを見る:
次の場所でコードを表示します。
現在、この機能は、16のネストされた関数の深さをサポートしていますが、現在、グローバル変数に色を付けていません。
JavaScriptには2種類のスコープしかありません。
var
キーワード付きの関数内で宣言された変数には、関数スコープがあります。関数が呼び出されると、変数スコープオブジェクトが作成され(スコープチェーンに含まれます)、JavaScriptで変数が続きます。
a = "global";
function outer(){
b = "local";
console.log(a+b); //"globallocal"
}
outer();
スコープチェーン->
a
およびouter
機能は、スコープチェーンの最上位にあります。variable scope object
変数で追加された新しい(スコープチェーンに含まれている)外部関数が呼び出されたときb
内部にがれたとき。変数がa
必要になると、最初に最も近い変数スコープを検索し、変数がそこにない場合は、変数スコープチェーンの次のオブジェクトに移動します。この場合はウィンドウレベルです。
他の答えに追加すると、スコープは宣言されたすべての識別子(変数)のルックアップリストであり、現在実行中のコードからこれらにアクセスする方法に関する一連の厳密なルールを適用します。このルックアップは、LHS(左側)参照である変数への割り当てを目的とする場合と、RHS(右側)参照であるその値を取得するためのものである場合があります。これらのルックアップは、コードをコンパイルして実行するときにJavaScriptエンジンが内部で実行していることです。
したがって、この観点から、カイルシンプソンによるScopes and Closures電子ブックで見つけた写真が役立つと思います。
彼の電子ブックからの引用:
建物は、プログラムのネストされたスコープルールセットを表しています。建物の1階は、どこにいても、現在実行中のスコープを表しています。建物のトップレベルはグローバルスコープです。LHSとRHSの参照を解決するには、現在のフロアを調べます。見つからない場合は、エレベーターで次のフロアに移動し、そこから次のフロアに移動します。最上階(グローバルスコープ)に到達すると、探しているものが見つかるか、見つからないかのどちらかです。しかし、あなたは関係なく停止する必要があります。
言及する価値のある注意点の1つは、「最初の一致が見つかるとスコープの検索が停止する」ことです。
この「スコープレベル」の考え方は、ネストされた関数でルックアップされている場合に、新しく作成されたスコープで「this」を変更できる理由を説明しています。これらのすべての詳細へのリンクは次のとおりです。JavaScriptスコープについて知りたいすべて
コードを実行します。これがスコープについてのアイデアを与えることを願っています
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);
グローバル変数はグローバルスターとまったく同じです(Jackie Chan、Nelson Mandela)。アプリケーションのどの部分からでも、それらにアクセス(値を取得または設定)できます。グローバルな機能は、グローバルなイベントのようなものです(新年、クリスマス)。アプリケーションのどの部分からでも実行(呼び出し)できます。
//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
ALMOSTには2種類のJavaScriptスコープしかありません。
したがって、関数以外のブロックは新しいスコープを作成しません。これが、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番目の例では、関数のために新しいスコープがあったため、最初に宣言された変数はシャドウされ、上書きされませんでした。
JavaScriptのスコープに関しては、次の点を除いて、これでほとんどすべて知ることができます。
したがって、JavaScriptのスコープは、必ずしも直感的ではありませんが、実際には非常に単純であることがわかります。注意すべき点がいくつかあります。
したがって、このコード:
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);
これは直感に反するように見えるかもしれませんが、命令型言語デザイナーの観点からは理にかなっています。
const
'および ' let
'他のほとんどの主要言語と同じように、作成するすべての変数にブロックスコープを使用する必要があります。var
ある時代遅れ。これにより、コードの安全性と保守性が向上します。
const
ケースの95%に使用する必要があります。変数参照を変更できないようにします。配列、オブジェクト、DOMノードのプロパティは変更される可能性があり、変更される可能性がありますconst
。
let
再割り当てされると予想されるすべての変数に使用する必要があります。これにはforループ内が含まれます。初期化を超えて値を変更する場合は、次を使用しますlet
。
ブロックスコープとは、変数が宣言されている括弧内でのみ変数を使用できることを意味します。これは、スコープ内で作成された無名関数を含む内部スコープにまで及びます。
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);
私の理解では、3つのスコープがあります。ブロックに関係なく、関数全体で使用できるローカルスコープ。ブロックスコープは、それが使用されたブロック、ステートメント、または式でのみ使用できます。グローバルスコープとローカルスコープは、関数内または外部のキーワード「var」で示され、ブロックスコープはキーワード「let」で示されます。
グローバルスコープとローカルスコープしかないと考えている人のために、MozillaがJSのブロックスコープのニュアンスを説明するページ全体を持っている理由を説明してください。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
フロントエンドのコーダーがよく遭遇する、まだ説明されていない非常に一般的な問題は、HTMLのインラインイベントハンドラーから見えるスコープです。たとえば、
<button onclick="foo()"></button>
on*
属性が参照できる変数のスコープは、次のいずれかである必要があります。
querySelector
スタンドアロン変数はを指すためdocument.querySelector
、まれ)そうしないと、ハンドラーが呼び出されたときにReferenceErrorが発生します。したがって、たとえば、インラインハンドラーが window.onload
or 内で定義された関数を$(function() {
参照する場合、インラインハンドラーはグローバルスコープの変数のみを参照でき、関数はグローバルではないため、参照は失敗します。
のプロパティ document
及びハンドラはインラインハンドラが呼び出されているため、また、インラインハンドラ内部スタンドアロン変数として参照することができるに取り付けられた要素の性質内側両者のwith
ブロック、のための1つのdocument
要素のための1つ。これらのハンドラー内の変数のスコープチェーンは非常に直感的ではなく、機能するイベントハンドラーはおそらく関数をグローバルにする必要があります(そして不要なグローバルな汚染はおそらく回避する必要があります)。
インラインハンドラー内のスコープチェーンは奇妙であり、インラインハンドラーはグローバルな汚染を必要とするため、また、インラインハンドラーは引数を渡すときに醜い文字列エスケープを必要とする場合があるため、おそらくそれらを回避する方が簡単です。代わりに、addEventListener
HTMLマークアップではなくJavaScriptを使用して(のように)イベントハンドラーをアタッチします。
別の注記では<script>
、トップレベルで実行される通常のタグとは異なり、ES6モジュール内のコードは独自のプライベートスコープで実行されます。通常の<script>
タグの先頭で定義されている変数はグローバルなので<script>
、次のように他のタグで参照できます。
しかし、ES6モジュールの最上位は はグローバルでません。ES6モジュールの上部で宣言された変数は、変数が明示的にexport
編集されていない限り、またはグローバルオブジェクトのプロパティに割り当てられていない限り、そのモジュール内でのみ表示されます。
ES6モジュールの最上位レベルは、通常の最上位レベルのIIFEの内部のものと同様です。 <script>
ます。モジュールはグローバルな変数を参照でき、モジュールが明示的に設計されていない限り、モジュール内の何も参照できません。
JavaScriptには、2種類のスコープがあります。
Below関数にはローカルスコープ変数がありますcarName
。また、この変数には関数の外部からアクセスできません。
function myFunction() {
var carName = "Volvo";
alert(carName);
// code here can use carName
}
Below Classには、グローバルスコープ変数がありますcarName
。そして、この変数はクラスのどこからでもアクセスできます。
class {
var carName = " Volvo";
// code here can use carName
function myFunction() {
alert(carName);
// code here can use carName
}
}
ES5
以前:Javascriptの変数は、当初(プリES6
)字句的に関数スコープでした。レキシカルスコープの用語は、コードを「調べる」ことによって変数のスコープを確認できることを意味します。
var
キーワードで宣言されたすべての変数は、関数をスコープとします。ただし、その関数内で他の関数が宣言されている場合、それらの関数は外部関数の変数にアクセスできます。これは、スコープチェーンと呼ばれます。次のように機能します。
// global scope
var foo = 'global';
var bar = 'global';
var foobar = 'global';
function outerFunc () {
// outerFunc scope
var foo = 'outerFunc';
var foobar = 'outerFunc';
innerFunc();
function innerFunc(){
// innerFunc scope
var foo = 'innerFunc';
console.log(foo);
console.log(bar);
console.log(foobar);
}
}
outerFunc();
何が私たちは、変数をログに記録しようとしているときに発生foo
、bar
と、foobar
コンソールに以下のとおりであります:
innerFunc
自体の中にあります。したがって、fooの値はstringに解決されますinnerFunc
。innerFunc
自体の中にbarが見つかりません。したがって、スコープチェーンを登る必要があります。最初に、関数innerFunc
が定義された外部関数を調べます。これが機能outerFunc
です。スコープ内にouterFunc
は、文字列「outerFunc」を保持する変数barがあります。ES6
(ES 2015)以前:レキシカルスコープとスコープチェーンの同じ概念がにも適用されES6
ます。ただし、変数を宣言する新しい方法が導入されました。以下があります。
let
:ブロックスコープ変数を作成しますconst
:初期化する必要があり、再割り当てできないブロックスコープ変数を作成します/ var
との最大の違いは、関数スコープであるのに対し、/ はブロックスコープであるということです。これを説明する例を次に示します。let
const
var
let
const
let letVar = 'global';
var varVar = 'global';
function foo () {
if (true) {
// this variable declared with let is scoped to the if block, block scoped
let letVar = 5;
// this variable declared with let is scoped to the function block, function scoped
var varVar = 10;
}
console.log(letVar);
console.log(varVar);
}
foo();
上記の例でlet
は、で宣言された変数はブロックスコープであるため、letVarは値globalをログに記録します。それらはそれぞれのブロックの外に存在しなくなるため、変数はifブロックの外にはアクセスできません。
EcmaScript5には主に2つのスコープ、ローカルスコープとグローバルスコープがありますが、EcmaScript6には主に3つのスコープ、ローカルスコープ、グローバルスコープ、およびブロックスコープと呼ばれる新しいスコープがあります。
ブロックスコープの例は:-
for ( let i = 0; i < 10; i++)
{
statement1...
statement2...// inside this scope we can access the value of i, if we want to access the value of i outside for loop it will give undefined.
}
ECMAScript 6では、letおよびconstキーワードが導入されました。これらのキーワードは、varキーワードの代わりに使用できます。varキーワードとは異なり、letおよびconstキーワードは、ブロックステートメント内のローカルスコープの宣言をサポートします。
var x = 10
let y = 10
const z = 10
{
x = 20
let y = 20
const z = 20
{
x = 30
// x is in the global scope because of the 'var' keyword
let y = 30
// y is in the local scope because of the 'let' keyword
const z = 30
// z is in the local scope because of the 'const' keyword
console.log(x) // 30
console.log(y) // 30
console.log(z) // 30
}
console.log(x) // 30
console.log(y) // 20
console.log(z) // 20
}
console.log(x) // 30
console.log(y) // 10
console.log(z) // 10
私は受け入れられた答えが本当に好きですが、これを追加したいと思います:
スコープは、宣言されたすべての識別子(変数)のルックアップリストを収集して維持し、現在実行中のコードからこれらにアクセスする方法に関する一連の厳密なルールを適用します。
スコープは、識別子名で変数を検索するための一連のルールです。
JavaScriptには2種類のスコープがあります。
グローバルスコープ:グローバルスコープでアナウンスされる変数は、プログラムのどこでも非常にスムーズに使用できます。例えば:
var carName = " BMW";
// code here can use carName
function myFunction() {
// code here can use carName
}
関数スコープまたはローカルスコープ:このスコープで宣言された変数は、独自の関数でのみ使用できます。例えば:
// code here can not use carName
function myFunction() {
var carName = "BMW";
// code here can use carName
}