JavaScriptの変数のスコープは何ですか?


2013

JavaScriptの変数のスコープは何ですか?関数の外側ではなく、内側に同じスコープがありますか?それとも問題ですか?また、グローバルに定義されている場合、変数はどこに保存されますか?


4
この問題を覚えておくためのもう1つの素晴らしいリンクは、「JavaScriptのスコープとクロージャーの説明」です。
フルスタックソフトウェアエンジニア

9
これは非常にうまく説明する記事です。Javascript変数スコープについて知っておくべきことすべて
Saurab Parakh

2
先に述べたカイル・シンプソンの電子書籍は、Githubの上で読むことが可能で、それはあなたがJavaScriptのスコープ&クロージャを知るために必要なすべてを説明します。あなたはそれをここで見つけることができます:github.com/getify/You-Dont-Know-JS/blob/master/…これは「知りませんJS」本シリーズの一部であり、知りたいすべての人に最適ですJavaScriptの詳細。
3rik82

回答:


2536

TLDR

JavaScriptには字句(静的とも呼ばれます)のスコープとクロージャーがあります。これは、ソースコードを見ることで識別子のスコープを知ることができることを意味します。

4つのスコープは次のとおりです。

  1. グローバル-すべてから見える
  2. 関数-関数(およびそのサブ関数とブロック)内で可視
  3. ブロック-ブロック(およびそのサブブロック)内に表示されます
  4. モジュール-モジュール内で表示

グローバルスコープとモジュールスコープの特別な場合を除いて、変数はvar(関数スコープ)、let(ブロックスコープ)、const(ブロックスコープ)を使用して宣言されます。他のほとんどの形式の識別子宣言は、strictモードでブロックスコープを持っています。

概観

スコープは、識別子が有効であるコードベースの領域です。

字句環境は、識別子名とそれに関連付けられた値の間のマッピングです。

スコープは、リンクされた字句環境のネストから形成され、ネストの各レベルは、祖先実行コンテキストの字句環境に対応しています。

これらのリンクされた字句環境は、スコープ「チェーン」を形成します。識別子の解決は、一致する識別子をこのチェーンに沿って検索するプロセスです。

識別子の解決は、一方向にのみ行われます。つまり、外側です。このようにして、外部の字句環境は内部の字句環境を「見る」ことができません。

JavaScriptで識別子スコープを決定するには、3つの適切な要素があります。

  1. 識別子の宣言方法
  2. 識別子が宣言された場所
  3. あなたはしているかどうかをstrictモードまたは非strictモード

識別子を宣言する方法のいくつか:

  1. varletおよびconst
  2. 関数パラメーター
  3. Catchブロックのパラメーター
  4. 関数宣言
  5. 名前付き関数式
  6. グローバルオブジェクトの暗黙的に定義されたプロパティ(つまりvar、非厳密モードでは欠落)
  7. import ステートメント
  8. eval

一部の場所識別子は宣言できます。

  1. グローバルコンテキスト
  2. 関数本体
  3. 通常のブロック
  4. 制御構造の上部(ループ、if、whileなど)
  5. 制御構造体
  6. モジュール

宣言スタイル

var

を使用しvar 宣言された識別子は、グローバルコンテキストで直接宣言されている場合を除いて、関数スコープ持っています。この場合、グローバルオブジェクトのプロパティとして追加され、グローバルスコープがあります。での使用には別のルールがありますeval関数でのます。

レットアンドコンスト

を使用let宣言され、ブロックスコープ持つ識別子const グローバルコンテキストで直接宣言されている場合を除い。この場合、グローバルスコープがあります。

注:letconstおよび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がスローされますxyおよび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)

次のコードはyand zに対してReferenceErrorをスローしますが、に対しては表示されません。これはx、の可視性がxブロックによって制約されていないためです。制御構造のボディを定義するブロックが好きifforそして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回印刷5console.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

を使用して宣言された変数varletまたは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では、スコープは外部参照によって「チェーン」でリンクされた字句環境を介して実装されます。この字句環境のチェーンはスコープチェーンと呼ばれ、識別子の解決はチェーンを検索することで行われます、一致する識別子のをます。

見つけるより


280
包括的というほどではありませんが、これはおそらく、最新のJavaScriptを効果的に読むために必要なJavaScriptスコープトリックの必知のセットです。
トリプティク

148
高い評価の答えです。理由はわかりません。これは、適切な説明のない単なる例の集まりであり、プロトタイプの継承(つまり、プロパティの解決)とスコープチェーン(つまり、変数の解決)を混同しているようです。スコープとプロパティの解決に関する包括的(かつ正確)な説明は、comp.lang.javascript FAQ notesにあります
RobG

109
@RobG幅広いプログラマーにとって有用であり、理解しやすいため、高い評価を得ています。あなたが投稿したリンクは、一部の専門家にとっては有用ですが、今日のJavaScriptを書いているほとんどの人にとっては理解不能です。回答を編集して、用語の問題を自由に修正してください。
トリプティク2012

7
@ triptych—私は回答を編集して、重大なものではなく、小さなことを修正します。「スコープ」を「プロパティ」に変更するとエラーは修正されますが、明確に区別されていない継承とスコープが混在する問題は修正されません。
RobG

24
外部スコープで変数を定義し、ifステートメントで同じ名前の関数内の変数を定義すると、if分岐に到達なかった場合でも再定義されます。例-jsfiddle.net/3CxVm
Chris S

233

JavaScriptはスコープチェーンを使用して、特定の関数のスコープを確立します。通常、1つのグローバルスコープがあり、定義された各関数には独自のネストされたスコープがあります。別の関数内で定義された関数には、外部関数にリンクされたローカルスコープがあります。スコープを定義するのは、常にソース内の位置です。

スコープチェーンの要素は、基本的には親スコープへのポインターを持つマップです。

変数を解決するとき、JavaScriptは最も内側のスコープから始まり、外側を検索します。


1
スコープチェーンは、[メモリ] クロージャの別の用語です...ここを読んでJavaScriptを習得するために。
新しいアレクサンドリア

108

グローバルに宣言された変数には、グローバルスコープがあります。関数内で宣言された変数は、その関数をスコープとし、同じ名前のグローバル変数をシャドウします。

(本当のJavaScriptプログラマーが他の答えで指摘できる多くの微妙な点があると確信しています。特に、いつでも正確に何が意味されるかについてこのページに出くわしましthisた。うまくいけば、このより多くの紹介リンクで十分に始められます。 。)


7
私もこの質問に答え始めることを恐れています。私は実際のJavaScriptプログラマーとして、答えが手に負えなくなるまでの時間を知っています。素敵な記事。
トリプティク

10
@Triptych:手に負えなくなったことについてあなたが何を意味しているのか知っていますが、とにかく答え追加してください。私は検索のカップルをやってから上記ました...実際の経験を持つ誰かによって書かれた答えがされてバインド良いことが。間違いでも間違っている私の答えを修正してください!
Jon Skeet、

4
どういうわけか、ジョンスキートは、スタックオーバーフローに関する私の最も人気のある回答の責任者です。
トリプティク2018年

75

古い学校のJavaScript

従来、JavaScriptには実際には2種類のスコープしかありません。

  1. グローバルスコープ:アプリケーションの最初から変数がアプリケーション全体で認識されます(*)
  2. 関数スコープ:変数は、関数の最初から、それらが宣言されている関数内で認識されます(*)

違いを説明する他の多くの回答がすでにあるため、これについては詳しく説明しません。


最新のJavaScript

最新のJavaScriptのスペックは、今も、第三のスコープを許可します:

  1. ブロックスコープ:識別子は、宣言されたスコープの先頭から「既知」ですが、宣言の行の後まで割り当てまたは逆参照(読み取り)できません。この暫定期間は、「一時的なデッドゾーン」と呼ばれます。

ブロックスコープ変数を作成するにはどうすればよいですか?

従来は、次のように変数を作成します。

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コードを記述していて、ブラウザーベースのトランスパイラー(Traceurbabel-standaloneなど)を使用している場合は、letステートメントを安全に使用できますが、コードはパフォーマンスに関して最適ではない可能性があります。

  • クライアント側のJavaScriptコードを記述していて、ノードベースのトランスパイラー(traceurシェルスクリプトBabelなど)を使用している場合は、letステートメントを安全に使用できます。また、ブラウザーはトランスパイルされたコードのみを認識するため、パフォーマンスの低下は制限されます。

  • クライアント側のJavaScriptコードを記述していて、トランスパイラーを使用しない場合は、ブラウザーのサポートを検討する必要があります。

    以下は、まったくサポートletしていないブラウザです。

    • Internet Explorer 10以下
    • Firefox 43以下
    • Safari 9以下
    • Androidブラウザー4以下
    • Opera 27以下
    • 40丁目以下
    • Opera MiniBlackberry Browserのすべてのバージョン

ここに画像の説明を入力してください


ブラウザのサポートを追跡する方法

letこの回答を読んだ時点でのステートメントをサポートしているブラウザーの最新の概要については、このCan I Useページを参照しください


(*)JavaScript変数がホイストされるため、グローバルおよび機能的にスコープされた変数は、宣言する前に初期化して使用できます。これは、宣言が常にスコープの一番上にあることを意味します。


2
変数が巻き上げのためにそこで宣言されているため、「ISが不明」は誤解を招く可能性があります。
Oriol

上記の例は誤解を招くものであり、変数「i」と「j」はブロックの外では認識されません。'Let'変数は、特定のブロックにのみスコープを持ち、ブロックの外側にはありません。他にも利点があります。変数を再宣言することはできず、レキシカルスコープを保持します。
zakir 2018年

1
これは役に立ちました、ありがとう!「モダンJavaScript」と「オールドスクールJavaScript」が何を意味するのかを具体的に説明することは、さらに役立つと思います。これらはそれぞれECMAScript 6 / ES6 / ECMAScript 2015と以前のバージョンに対応していると思いますか?
Jon Schneider

1
@JonSchneider:正解です!私が「古いJavaScript」と言う場合、私はECMAScript 5について話しているのにうんざりしており、「モダンJavaScript」について言及している場合、私はECMAScript 6(別名ECMAScript 2015)について取り上げています。ただし、ほとんどの人は(1)ブロックスコープと機能スコープの違いは何か、(2)どのブラウザーがブロックスコープをサポートしているか、(3)を知りたいだけなので、ここで詳しく説明することはそれほど重要だとは思いませんでした。彼らが取り組んでいるどんなプロジェクトのために今日でもブロックスコープを使用することが安全であるかどうか。それで私はそれらの問題に取り組むことに私の答えを集中させました。
John Slegers 2018年

1
@JonSchneider:(続き)それでも、過去6年間にJavaScriptに追加された機能について詳しく知りたい人のために、ES6 / ES2015のSmashing Magazine記事へのリンクを追加しました... 「モダンJavaScript」で私が何を意味するのか疑問に思うかもしれません。
John Slegers 2018年

39

次に例を示します。

<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>

クロージャーと、それらを使用してプライベートメンバーにする方法を調査する必要があります。



26

「Javascript 1.7」(MozillaのJavascriptへの拡張)では、letステートメントでブロックスコープ変数を宣言することもできます。

 var a = 4;
 let (a = 3) {
   alert(a); // 3
 }
 alert(a);   // 4

2
ええ、でも安全に使えますか?私のコードがWebKitで実行される場合、この実装を現実的に選択するということですか?
IgorGanapolsky 2010

10
@Python:いいえ、WebKitはをサポートしていませんlet
kennytm

これの唯一の有効な使用法は、すべてのクライアントが企業の内部システムのようにMozillaブラウザを使用することを知っている場合でしょう。
GazB 2012年

または、XULフレームワークを使用してプログラミングしている場合は、css、xml、およびjavascriptを使用してビルドするMozillaのインターフェースフレームワーク。
Gerard ONeill 2013年

1
@GazBでもそれは恐ろしい考えです!したがって、今日、クライアントがMozillaを使用していることを知った後、別の何かを使用していることを示す新しいメモが出てきます。IE有料システムが悪い理由... IE8を使用する必要があります。フラットアウトが機能しないため、IE9、IE10、Firefox、Chromeは使用しないでください...
buzzsawddog

25

JavaScriptでスコープするというアイデアは、もともとブレンダンアイヒによって設計されたときに、HyperCardスクリプト言語HyperTalkから生まれました。

この言語では、表示はインデックスカードのスタックと同様に行われました。背景と呼ばれるマスターカードがありました。それは透明で、一番下のカードとして見ることができます。この基本カードのコンテンツは、その上に配置されたカードと共有されました。一番上に置かれた各カードには、前のカードよりも優先される独自のコンテンツがありましたが、必要に応じて以前のカードにアクセスできました。

これがまさにJavaScriptスコープシステムの設計方法です。名前が違うだけです。JavaScriptのカードは、実行コンテキストECMAとして知られていますます。これらの各コンテキストには、3つの主要な部分があります。変数環境、字句環境、およびthisバインディング。カードのリファレンスに戻ると、字句環境には、スタックの下位にある以前のカードのすべてのコンテンツが含まれています。現在のコンテキストはスタックの最上位にあり、そこで宣言されたコンテンツはすべて変数環境に格納されます。名前の衝突が発生した場合は、変数環境が優先されます。

thisバインディングは、それを含むオブジェクトを指します。含まれているオブジェクトが存在する可能性のある宣言された関数windowやコンストラクター関数など、含まれているオブジェクトを変更せずにスコープまたは実行コンテキストが変更される場合があります。

これらの実行コンテキストは、コントロールが転送されるたびに作成されます。コードが実行を開始すると制御が移り、これは主に関数の実行から行われます。

これが技術的な説明です。実際には、JavaScriptでは

  • スコープは技術的には「実行コンテキスト」です
  • コンテキストは、変数が格納される環境のスタックを形成します
  • スタックの一番上が優先されます(一番下がグローバルコンテキストです)。
  • 各関数は実行コンテキストを作成します(ただし、常に新しいthisバインディングではありません)

これをこのページの前の例(5.「クロージャー」)の1つに適用すると、実行コンテキストのスタックをたどることができます。この例では、スタックに3つのコンテキストがあります。それらは、外側のコンテキスト、var sixによって呼び出される即時に呼び出される関数のコンテキスト、およびvar sixの即時に呼び出される関数内の返される関数のコンテキストによって定義されます。

i)外部コンテキスト。それはa = 1の変数環境を持っています
ii)IIFEコンテキスト、それはa = 1の字句環境を持っていますが、スタックで優先されるa = 6の変数環境
iii)返された関数コンテキスト、それは字句を持っていますa = 6の環境。これは、呼び出されたときにアラートで参照される値です。

ここに画像の説明を入力してください


17

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です!すでに定義されている名前に少し手を加えると、これははるかに安全になります。このため個人的には使用しません。


1つのJavaScriptにはブロックスコープの形式があるため、ここでいくつかの間違いがあります。
Benjamin Gruenbaum 2013年

私の耳(目)は開いています、ベンジャミン-上記の私の発言は、私がJavaScriptスコーピングを扱ってきた方法ですが、それらは仕様を読むことに基づいていません。そして、withステートメント(オブジェクトスコープの形式)や、Mozillaの特別な「let」構文を参照していないことを願っています。
Gerard ONeill 2013年

まあ、withステートメントブロックスコープの形式ですが、catch句はより一般的な形式です(楽しい事実、v8はで実装さcatchれますwith)-JavaScript自体のブロックスコープの唯一の形式です(つまり、関数、グローバル、try / catchです)。 、およびその派生物)、ただし、ホスト環境ではスコープの概念が異なります。たとえば、ブラウザーのインラインイベントやNodeJSのvmモジュールなどです。
Benjamin Gruenbaum 2013年

ベンジャミン-私が見ることができるものから、withとcatchの両方が現在のスコープ(したがってプロパティ)にオブジェクトを導入するだけですが、それぞれのブロックが終了した後、変数がリセットされます。しかし、たとえば、catchに導入された新しい変数は、それを囲む関数/メソッドのスコープを持ちます。
Gerard ONeill 2013年

2
これがまさにブロックスコーピングの意味です:)
Benjamin Gruenbaum 2013年

9

JavaScriptを初めて使う人の多くは、言語で継承がデフォルトで利用可能であり、関数スコープがこれまでのところ唯一のスコープであることを理解するのに苦労していることがわかりました。昨年末に書いた美容師の拡張機能としてJSPrettyを提供しました。機能はコード内の関数スコープを色分けし、常にそのスコープで宣言されたすべての変数に色を関連付けます。あるスコープの色を持つ変数が別のスコープで使用されると、クロージャが視覚的に示されます。

この機能を試してください:

デモを見る:

次の場所でコードを表示します。

現在、この機能は、16のネストされた関数の深さをサポートしていますが、現在、グローバル変数に色を付けていません。


1
Firefox 26では動作しません。コードを貼り付けるかファイルをロードし、[実行]をクリックしても何も起こりません。
mplwork 14

スコープと継承は2つの違いです。
ベンアストン

9

JavaScriptには2種類のスコープしかありません。

  1. グローバルスコープ:グローバルはウィンドウレベルのスコープにすぎません。ここでは、アプリケーション全体に存在する変数です。
  2. 関数スコープvarキーワード付きの関数内で宣言された変数には、関数スコープがあります。

関数が呼び出されると、変数スコープオブジェクトが作成され(スコープチェーンに含まれます)、JavaScriptで変数が続きます。

        a = "global";
         function outer(){ 
              b = "local";
              console.log(a+b); //"globallocal"
         }
outer();

スコープチェーン->

  1. ウィンドウレベル- aおよびouter機能は、スコープチェーンの最上位にあります。
  2. variable scope object変数で追加された新しい(スコープチェーンに含まれている)外部関数が呼び出されたときb内部にがれたとき。

変数がa必要になると、最初に最も近い変数スコープを検索し、変数がそこにない場合は、変数スコープチェーンの次のオブジェクトに移動します。この場合はウィンドウレベルです。


1
なぜこれが受け入れられない答えであるのかわからない。実際には、機能的なスコープ(ECMA6より前には「ローカルスコープ」はありませんでした)とグローバルなバインディングのみが存在します
texasbruce

9

他の答えに追加すると、スコープは宣言されたすべての識別子(変数)のルックアップリストであり、現在実行中のコードからこれらにアクセスする方法に関する一連の厳密なルールを適用します。このルックアップは、LHS(左側)参照である変数への割り当てを目的とする場合と、RHS(右側)参照であるその値を取得するためのものである場合があります。これらのルックアップは、コードをコンパイルして実行するときにJavaScriptエンジンが内部で実行していることです。

したがって、この観点から、カイルシンプソンによるScopes and Closures電子ブックで見つけた写真が役立つと思います。

画像

彼の電子ブックからの引用:

建物は、プログラムのネストされたスコープルールセットを表しています。建物の1階は、どこにいても、現在実行中のスコープを表しています。建物のトップレベルはグローバルスコープです。LHSとRHSの参照を解決するには、現在のフロアを調べます。見つからない場合は、エレベーターで次のフロアに移動し、そこから次のフロアに移動します。最上階(グローバルスコープ)に到達すると、探しているものが見つかるか、見つからないかのどちらかです。しかし、あなたは関係なく停止する必要があります。

言及する価値のある注意点の1つは、「最初の一致が見つかるとスコープの検索が停止する」ことです。

この「スコープレベル」の考え方は、ネストされた関数でルックアップされている場合に、新しく作成されたスコープで「this」を変更できる理由を説明しています。これらのすべての詳細へのリンクは次のとおりです。JavaScriptスコープについて知りたいすべて


8

コードを実行します。これがスコープについてのアイデアを与えることを願っています

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);

8

グローバルスコープ:

グローバル変数はグローバルスターとまったく同じです(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    

スコープの詳細については、この記事を確認してください


6

ALMOSTには2種類のJavaScriptスコープしかありません。

  • 各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番目の例では、関数のために新しいスコープがあったため、最初に宣言された変数はシャドウされ、上書きされませんでした。

JavaScriptのスコープに関しては、次の点を除いて、これでほとんどすべて知ることができます。

したがって、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);

これは直感に反するように見えるかもしれませんが、命令型言語デザイナーの観点からは理にかなっています。


5

モダンJs、ES6 +、 ' const'および ' let'

他のほとんどの主要言語と同じように、作成するすべての変数にブロックスコープを使用する必要があります。varある時代遅れ。これにより、コードの安全性と保守性が向上します。

constケースの95%に使用する必要があります。変数参照を変更できないようにします。配列、オブジェクト、DOMノードのプロパティは変更される可能性があり、変更される可能性がありますconst

let再割り当てされると予想されるすべての変数に使用する必要があります。これにはforループ内が含まれます。初期化を超えて値を変更する場合は、次を使用しますlet

ブロックスコープとは、変数が宣言されている括弧内でのみ変数を使用できることを意味します。これは、スコープ内で作成された無名関数を含む内部スコープにまで及びます。


3

この奇妙な例を試してください。以下の例では、aが0で初期化された数値の場合、0と1が表示されます。ただし、aはオブジェクトであり、JavaScriptはf1にそのコピーではなくaのポインタを渡します。その結果、同じアラートが両方とも取得されます。

var a = new Date();
function f1(b)
{
    b.setDate(b.getDate()+1);
    alert(b.getDate());
}
f1(a);
alert(a.getDate());

3

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);

(回答が投稿されてから長い時間がかかります)ブロックスコープ。developer.mozilla.org/en/docs/Web/JavaScript/Reference/...
ボブ

2

私の理解では、3つのスコープがあります。ブロックに関係なく、関数全体で使用できるローカルスコープ。ブロックスコープは、それが使用されたブロック、ステートメント、または式でのみ使用できます。グローバルスコープとローカルスコープは、関数内または外部のキーワード「var」で示され、ブロックスコープはキーワード「let」で示されます。

グローバルスコープとローカルスコープしかないと考えている人のために、MozillaがJSのブロックスコープのニュアンスを説明するページ全体を持っている理由を説明してください。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let


2

フロントエンドのコーダーがよく遭遇する、まだ説明されていない非常に一般的な問題は、HTMLのインラインイベントハンドラーから見えるスコープです。たとえば、

<button onclick="foo()"></button>

on*属性が参照できる変数のスコープは、次のいずれかである必要があります。

  • グローバル(作業中のインラインハンドラーはほとんど常にグローバル変数を参照します)
  • ドキュメントのプロパティ(たとえば、querySelectorスタンドアロン変数はを指すためdocument.querySelector、まれ)
  • ハンドラーがアタッチされている要素のプロパティ(上記のように、まれです)

そうしないと、ハンドラーが呼び出されたときにReferenceErrorが発生します。したがって、たとえば、インラインハンドラー window.onload or で定義された関数を$(function() {参照する場合、インラインハンドラーはグローバルスコープの変数のみを参照でき、関数はグローバルではないため、参照は失敗します。

のプロパティ document及びハンドラはインラインハンドラが呼び出されているため、また、インラインハンドラ内部スタンドアロン変数として参照することができるに取り付けられた要素の性質内側両者のwithブロック、のための1つのdocument要素のための1つ。これらのハンドラー内の変数のスコープチェーンは非常に直感的ではなく、機能するイベントハンドラーはおそらく関数をグローバルにする必要があります(そして不要なグローバルな汚染はおそらく回避する必要があります)。

インラインハンドラー内のスコープチェーンは奇妙であり、インラインハンドラーはグローバルな汚染を必要とするため、また、インラインハンドラーは引数を渡すときに醜い文字列エスケープを必要とする場合があるため、おそらくそれらを回避する方が簡単です。代わりに、addEventListenerHTMLマークアップではなくJavaScriptを使用して(のように)イベントハンドラーをアタッチします。


別の注記では<script>、トップレベルで実行される通常のタグとは異なり、ES6モジュール内のコードは独自のプライベートスコープで実行されます。通常の<script>タグの先頭で定義されている変数はグローバルなので<script>、次のように他のタグで参照できます。

しかし、ES6モジュールの最上位は グローバルでません。ES6モジュールの上部で宣言された変数は、変数が明示的にexport編集されていない限り、またはグローバルオブジェクトのプロパティに割り当てられていない限り、そのモジュール内でのみ表示されます。

ES6モジュールの最上位レベルは、通常の最上位レベルのIIFEの内部のものと同様です。 <script>ます。モジュールはグローバルな変数を参照でき、モジュールが明示的に設計されていない限り、モジュール内の何も参照できません。


1

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 
    }
}

1

ES5 以前:

Javascriptの変数は、当初(プリES6)字句的に関数スコープでした。レキシカルスコープの用語は、コードを「調べる」ことによって変数のスコープを確認できることを意味します。

varキーワードで宣言されたすべての変数は、関数をスコープとします。ただし、その関数内で他の関数が宣言されている場合、それらの関数は外部関数の変数にアクセスできます。これは、スコープチェーンと呼ばれます。次のように機能します。

  1. 関数が変数値を解決しようとするとき、関数はまず自身のスコープを調べます。これは関数本体です。つまり、中括弧{}の間のすべて(このスコープにある他の 関数変数を除く)。
  2. 関数本体内で変数が見つからない場合は、チェーンに登り、関数が定義されている関数の変数スコープを調べます。これが字句スコープの意味です。この関数が定義されているコードで確認できるため、コードを見るだけでスコープチェーンを特定できます。

例:

// 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();

何が私たちは、変数をログに記録しようとしているときに発生foobarと、foobarコンソールに以下のとおりであります:

  1. fooをコンソールに記録しようとします。fooは関数innerFunc自体の中にあります。したがって、fooの値はstringに解決されますinnerFunc
  2. barをコンソールに記録しようとしましたが、関数innerFunc自体の中にbarが見つかりません。したがって、スコープチェーン登る必要があります。最初に、関数innerFuncが定義された外部関数を調べます。これが機能outerFuncです。スコープ内にouterFuncは、文字列「outerFunc」を保持する変数barがあります。
  3. foob​​arがinnerFuncに見つかりません。。したがって、スコープチェーンをinnerFuncスコープに登る必要があります。それもここでは見つかりません。グローバルスコープ(つまり、最も外側のスコープ)に別のレベルを登ります。ここで、文字列「global」を保持する変数foobarを見つけます。スコープチェーンを上った後も変数が見つからなかった場合、JSエンジンはreferenceErrorをスローします

ES6 (ES 2015)以前:

レキシカルスコープとスコープチェーンの同じ概念がにも適用されES6ます。ただし、変数を宣言する新しい方法が導入されました。以下があります。

  • let:ブロックスコープ変数を作成します
  • const:初期化する必要があり、再割り当てできないブロックスコープ変数を作成します

/ varとの最大の違いは、関数スコープであるのに対し、/ はブロックスコープであるということです。これを説明する例を次に示します。letconstvarletconst

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ブロックの外にはアクセスできません。


0

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.
}

0

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

0

私は受け入れられた答えが本当に好きですが、これを追加したいと思います:

スコープは、宣言されたすべての識別子(変数)のルックアップリストを収集して維持し、現在実行中のコードからこれらにアクセスする方法に関する一連の厳密なルールを適用します。

スコープは、識別子名で変数を検索するための一連のルールです。

  • 変数が直接のスコープで見つからない場合、エンジンは次の外側のスコープを調べ、見つかるまで、または最も外側の(別名、グローバル)スコープに到達するまで続行します。
  • 変数(識別子)を検索する場所と方法を決定する一連のルールです。このルックアップは、LHS(左側)参照である変数への割り当てを目的とする場合と、RHS(右側)参照であるその値を取得するためのものである場合があります。 。
  • LHS参照は、割り当て操作の結果として生じます。スコープ関連の割り当ては、=演算子を使用するか、または関数パラメーターに引数を(割り当てる)渡すことによって行うことができます。
  • JavaScriptエンジンは、実行する前にまずコードをコンパイルし、その際にvar a = 2のようなステートメントを分割します。2つの個別のステップに:1番目。まず、そのスコープで宣言するvar a。これは、コード実行前の最初に実行されます。2番目。その後、a = 2は変数(LHS参照)を検索し、見つかった場合は変数に割り当てます。
  • LHSとRHSの両方の参照ルックアップは、現在実行中のスコープから始まり、必要に応じて(つまり、探しているものが見つからない場合)、ネストされたスコープ、つまり1つのスコープ(floor )一度に、グローバル(最上階)に到達して停止するまで識別子を探し、停止してそれを見つけるか、見つけません。RHS参照が満たされていないと、ReferenceErrorがスローされます。満たされていないLHS参照は、自動的に暗黙的に作成されたその名前のグローバル(厳密モードでない場合)、またはReferenceError(厳密モードの場合)になります。
  • スコープは、それぞれが識別子(変数、関数)が宣言されるコンテナまたはバケットとして機能する一連の「バブル」で構成されます。これらのバブルはお互いの内側にきちんと入れ子になっており、この入れ子は作成時に定義されています。

-3

JavaScriptには2種類のスコープがあります。

  1. グローバルスコープグローバルスコープでアナウンスされる変数は、プログラムのどこでも非常にスムーズに使用できます。例えば:

    var carName = " BMW";
    
    // code here can use carName
    
    function myFunction() {
         // code here can use carName 
    }
  2. 関数スコープまたはローカルスコープ:このスコープで宣言された変数は、独自の関数でのみ使用できます。例えば:

    // code here can not use carName
    function myFunction() {
       var carName = "BMW";
       // code here can use carName
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.