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

2058
lYriCAlsSH 2009-02-01 22:27.

javascriptの変数のスコープは何ですか?関数の外部ではなく、内部で同じスコープを持っていますか?それとも重要ですか?また、グローバルに定義されている場合、変数はどこに保存されますか?

25 answers

2571
Triptych 2009-02-01 22:58.

TLDR

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

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

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

グローバルスコープとモジュールスコープの特殊なケース以外では、変数はvar(関数スコープ)、let(ブロックスコープ)、およびconst(ブロックスコープ)を使用して宣言されます。他のほとんどの形式の識別子宣言には、厳密モードのブロックスコープがあります。

概要概要

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

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

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

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

識別子の解決は一方向、つまり外向きにのみ発生します。このように、外部の語彙環境は内部の語彙環境を「見る」ことができません。

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

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

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

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

場所識別子のいくつかは宣言できます:

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

宣言スタイル

var

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

letとconst

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

注:letconstおよびvar すべての掲揚されています。これは、定義の論理的な位置が、囲んでいるスコープ(ブロックまたは関数)の最上位であることを意味します。ただし、変数が使用して宣言letし、const読み出しまたは制御は、ソースコード内の宣言のポイントを通過するまでに割り当てることはできません。暫定期間は、一時的なデッドゾーンとして知られています。

function f() {
    function g() {
        console.log(x)
    }
    let x = 1
    g()
}
f() // 1 because x is hoisted even though declared with `let`!

関数のパラメーター名

関数パラメーター名は、関数本体にスコープされます。これには少し複雑な点があることに注意してください。デフォルトの引数として宣言された関数は、関数の本体ではなく、パラメーターリストの近くにあります。

関数宣言

関数宣言には、厳密モードではブロックスコープがあり、非厳密モードでは関数スコープがあります。注:非厳密モードは、さまざまなブラウザーの風変わりな歴史的実装に基づく複雑な一連の緊急ルールです。

名前付き関数式

名前付き関数式は、それ自体にスコープが設定されます(たとえば、再帰の目的で)。

グローバルオブジェクトで暗黙的に定義されたプロパティ

非厳密モードでは、グローバルオブジェクトはスコープチェーンの最上位にあるため、グローバルオブジェクトに暗黙的に定義されたプロパティにはグローバルスコープがあります。厳密モードでは、これらは許可されていません。

eval

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)

以下は、の可視性がブロックによって制約されていないため、yおよびのReferenceErrorをスローしますが、のはスローしません。制御構造のボディを定義するブロックが好き、と、同様に振る舞います。zxxifforwhile

{
    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!)

...この動作のため、varinループを使用して宣言された変数を閉じることに注意する必要があります。xここで宣言されている変数のインスタンスは1つだけであり、論理的にループの外側にあります。

次のように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

以下はundefinedxがブロックスコープであるために出力されます。コールバックは非同期で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

キャッチブロックパラメータは、キャッチブロック本体にスコープされます。

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

非厳密モードでは、関数宣言には関数スコープがあります。strictモードでは、ブロックスコープがあります。

'use strict'
{
    function foo() {}
}
console.log(typeof foo) // undefined, because `foo` is block-scoped

内部でどのように機能するか

スコープは、識別子が有効なコードの字句領域として定義されます。

JavaScriptでは、すべての関数オブジェクトには、それが作成された実行コンテキスト(スタックフレーム)[[Environment]]の字句環境への参照である非表示の参照があります。

関数を呼び出すと、hidden[[Call]]メソッドが呼び出されます。このメソッドは、新しい実行コンテキストを作成し、新しい実行コンテキストと関数オブジェクトの字句環境との間にリンクを確立します。これは[[Environment]]、関数オブジェクトの値を、新しい実行コンテキストの字句環境の外部参照フィールドにコピーすることによって行われます。

新しい実行コンテキストと関数オブジェクトの字句環境の間のこのリンクは、クロージャと呼ばれることに注意してください。

したがって、JavaScriptでは、スコープは、外部参照によって「チェーン」にリンクされた字句環境を介して実装されます。この字句環境のチェーンはスコープチェーンと呼ばれ、識別子の解決は、一致する識別子をチェーンで検索することによって行われます。

見つけるより。

234
krosenvold 2009-02-01 22:35.

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

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

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

110
Jon Skeet 2009-02-01 22:31.

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

(実際のJavaScriptプログラマーが他の回答で指摘できる微妙な点はたくさんあると思います。特に、いつでも正確に何を意味するのかについてこのページに出くわしましthisた。このより紹介的なリンクで十分に始められるといいのですが。 。)

77
John Slegers 2016-02-24 08:51.

古い学校の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コードを記述していて、ノードベースのトランスパイラー(トレーサーシェルスクリプトBabelなど)を使用している場合は、このletステートメントを安全に使用できます。また、ブラウザはトランスパイルされたコードしか認識しないため、パフォーマンスの欠点を制限する必要があります。

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

    これらはまったくサポートletしていないいくつかのブラウザです:

    • Internet Explorer10以下
    • Firefox43以下
    • Safari9以下
    • Androidブラウザ4以下
    • Opera27以下
    • チョーム40以下
    • いずれかのバージョンのOpera Miniブラックベリーのブラウザ


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

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


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

40
geowa4 2009-02-01 22:48.

次に例を示します。

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

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

31
James McMahon 2012-05-16 07:38.

重要なのは、私が理解しているように、Javascriptには関数レベルのスコープとより一般的なCブロックのスコープがあるということです。

これはこのテーマに関する良い記事です。

26
kennytm 2010-04-07 01:19.

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

 var a = 4;
 let (a = 3) {
   alert(a); // 3
 }
 alert(a);   // 4
25
Travis J 2015-09-15 10:29.

Brendan Eichによって最初に設計されたときのJavaScriptでのスコープのアイデアは、HyperCardスクリプト言語HyperTalkから生まれました。

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

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

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

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

これが技術的な説明です。実際には、JavaScriptではそれを覚えておくことが重要です

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

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

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

17
Gerard ONeill 2013-10-25 14:41.

1)グローバルスコープ、関数スコープ、およびwithスコープとcatchスコープがあります。一般に、変数には「ブロック」レベルのスコープはありません。withステートメントとcatchステートメントは、ブロックに名前を追加します。

2)スコープは、関数によってグローバルスコープまでネストされます。

3)プロパティは、プロトタイプチェーンを通過することによって解決されます。withステートメントは、オブジェクトプロパティ名をwithブロックで定義された字句スコープに取り込みます。

編集:ECMAAScript 6(Harmony)はletをサポートするように指定されており、chromeは「ハーモニー」フラグを許可していることを知っているので、おそらくそれをサポートしています。

ブロックレベルのスコープのサポートになりますが、それを実現するにはキーワードを使用する必要があります。

編集:コメント内のwithステートメントとcatchステートメントをBenjaminが指摘していることに基づいて、投稿を編集し、さらに追加しました。どちらにし、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を作成します。

うわぁ!'with'の使用には注意してください-変数が関数ですでに定義されている場合にvarがnoopであるのと同様に、オブジェクトからインポートされた名前に関してもnoopです!すでに定義されている名前について少し注意を払えば、これははるかに安全になります。このため、個人的には絶対に使用しません。

9
austincheney 2013-03-22 07:31.

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

次の場所で機能を試してください。

  • http://prettydiff.com/jspretty.xhtml?c=white&jsscope

次のデモを参照してください。

  • http://prettydiff.com/jspretty.xhtml?c=white&jsscope&s=http://prettydiff.com/lib/markup_beauty.js

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

  • http://prettydiff.com/lib/jspretty.js
  • https://github.com/austincheney/Pretty-Diff/blob/master/lib/jspretty.js

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

9
Anshul 2014-09-22 10:44.

JavaScriptには2つのタイプのスコープしかありません:

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

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

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

スコープチェーン->

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

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

9
James Drinkard 2016-03-31 03:33.

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

したがって、この観点から、カイル・シンプソンの「スコープとクロージャ」の電子ブックで見つけた写真が役立つと思います。

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

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

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

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

8
Yeasin Abedin Siam 2014-10-18 23:54.

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

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
Jhankar Mahbub 2014-06-25 04:12.

グローバルスコープ:

グローバル変数は、グローバルスター(ジャッキーチェン、ネルソンマンデラ)とまったく同じです。アプリケーションのどの部分からでも、それらにアクセス(値を取得または設定)できます。グローバルな機能は、グローバルなイベント(新年、クリスマス)のようなものです。アプリケーションのどの部分からでも実行(呼び出し)できます。

//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
jackbean818 2015-10-30 06:12.

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

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

4
Gibolt 2017-09-27 10:23.

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

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

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

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

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

3
Mig82 2013-08-11 07:37.

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

var a = new Date();
function f1(b)
{
    b.setDate(b.getDate()+1);
    alert(b.getDate());
}
f1(a);
alert(a.getDate());
3
koredalin 2015-07-12 08:03.

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);
2
mrmaclean89 2017-09-17 12:35.

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

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

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

2
Abdur Rahman 2017-11-01 23:16.

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

  • ローカルスコープ
  • グローバルスコープ

以下の関数には、ローカルスコープ変数がありますcarName。また、この変数には関数の外部からアクセスすることはできません。

function myFunction() {
    var carName = "Volvo";
    alert(carName);
    // code here can use carName
}

以下のクラスには、グローバルスコープ変数がありますcarName。そして、この変数はクラスのどこからでもアクセスできます。

class {

    var carName = " Volvo";

    // code here can use carName

    function myFunction() {
        alert(carName);
        // code here can use carName 
    }
}
2
CertainPerformance 2019-12-31 16:50.

フロントエンドコーダーが頻繁に遭遇する、まだ説明されていない非常に一般的な問題は、HTMLのインラインイベントハンドラーに表示されるスコープです。

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

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

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

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

window.addEventListener('DOMContentLoaded', () => {
  function foo() {
    console.log('foo running');
  }
});
<button onclick="foo()">click</button>

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

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

function foo() {
  console.log('foo running');
}
document.querySelector('.my-button').addEventListener('click', foo);
<button class="my-button">click</button>


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

<script>
const foo = 'foo';
</script>
<script>
console.log(foo);
</script>

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

<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>ます。モジュールはグローバルな変数を参照でき、モジュールが明示的に設計されていない限り、モジュール内の変数を参照することはできません。

1
Willem van der Veen 2018-09-30 23:23.

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

ES6 (ES 2015)以前:

字句スコープとスコープチェーンの同じ概念は、でも適用されES6ます。ただし、変数を宣言する新しい方法が導入されました。次のものがあります。

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

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

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は値をグローバルにログに記録します。それらはそれぞれのブロックの外側に存在しなくなるため、ifブロックの外側で変数にアクセスすることはできません。

0
Vivek Mehta 2017-12-15 19:31.

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
Davaakhuu Erdenekhuu 2018-02-10 05:37.

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
Ahmed Khashaba 2020-05-26 06:11.

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

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

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

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

Related questions

MORE COOL STUFF

ケイト・ブランシェットは3日間一緒に夫と一緒に寝て、25年経ってもまだ夫と結婚しています

ケイト・ブランシェットは3日間一緒に夫と一緒に寝て、25年経ってもまだ夫と結婚しています

ケイト・ブランシェットは、夫に会ったとき、典型的な交際のアドバイスに逆らいました。

マイケルシーンが非営利の俳優である理由

マイケルシーンが非営利の俳優である理由

マイケルシーンは非営利の俳優ですが、それは正確にはどういう意味ですか?

ホールマークスターのコリンエッグレスフィールドがRomaDramaLiveでスリル満点のファンと出会う![エクスクルーシブ]

ホールマークスターのコリンエッグレスフィールドがRomaDramaLiveでスリル満点のファンと出会う![エクスクルーシブ]

特徴的なスターのコリン・エッグレスフィールドは、RomaDrama Liveでのスリル満点のファンとの出会いについて料理しました!加えて、大会での彼のINSPIREプログラム。

「たどりつけば」をオンラインでストリーミングできない理由

「たどりつけば」をオンラインでストリーミングできない理由

ノーザンエクスポージャーが90年代の最も人気のある番組の1つになった理由を確認するには、Blu-rayまたはDVDプレーヤーをほこりで払う必要があります。

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

ドミニカのボイリング湖:アクセスは簡単ではありませんが、ハイキングする価値があります

ドミニカのボイリング湖:アクセスは簡単ではありませんが、ハイキングする価値があります

ドミニカのボイリング湖は、世界で2番目に大きいボイリング湖です。そこにたどり着くまでのトレッキングは大変で長いですが、努力する価値は十分にあります。

私たちの水をきれいに保つのを助けるためにあなたの髪を寄付してください

私たちの水をきれいに保つのを助けるためにあなたの髪を寄付してください

サロンからのヘアトリミングや個人的な寄付は、油流出を吸収して環境を保護するのに役立つマットとして再利用できます。

ホワイトハウスの最も記憶に残る結婚式を見てください

ホワイトハウスの最も記憶に残る結婚式を見てください

過去200年以上の間にホワイトハウスで結婚したのはほんの数人です。彼らは誰でしたか、そしてそこで結婚式を獲得するために何が必要ですか?

控訴裁判所によって取り下げられたメタン規制の施行を遅らせるEPAの試み

控訴裁判所によって取り下げられたメタン規制の施行を遅らせるEPAの試み

ゲッティ経由の画像。スコット・プルイットは環境保護庁の長であり、二酸化炭素とメタンガスとの愛情のこもった関係を持っていますが、彼は地球温暖化を信じることに冷たい肩を与えています。

メッセージングアプリの「誰かが入力している」バブルが実際に意味するもの

メッセージングアプリの「誰かが入力している」バブルが実際に意味するもの

一連の点滅する省略記号であろうと、「誰かが入力している」という明示的な通知であろうと、入力インジケーターと呼ばれるメッセージング機能は、多くの人にとって便利なツールであり、不安の原因でもあります。しかし、それがどのように機能するかについてもっと知ることは、少なくとも私たちにストレスを引き起こす推測ゲームを制限することができます。

マット・ラウアーは、離婚の和解で数百万ドルと馬の農場を失うことに「激怒」したと伝えられています

マット・ラウアーは、離婚の和解で数百万ドルと馬の農場を失うことに「激怒」したと伝えられています

マット・ラウアーと彼の妻のアネット・ロークは結婚間近であり、離婚手続きが激化していると伝えられています。ある情報筋は、ラウアーが「和解で約5,000万ドルをフォークする予定」であり、「マットは激怒している」と語った。彼は本質的に彼の純資産の半分をアネットに手渡している。

まあ、これは厄介です:レイプ告発者はケレンウィンズロージュニアの弁護士を彼女の攻撃者として特定します

まあ、これは厄介です:レイプ告発者はケレンウィンズロージュニアの弁護士を彼女の攻撃者として特定します

2018年6月15日金曜日、カリフォルニア州ビスタでの罪状認否の際、元NFLフットボール選手のケレンウィンズロージュニア(中央)が弁護士のブライアンワトキンス(左)とハーベイスタインバーグの後ろに立っています。

Zendaya Wishes Boyfriend Tom Holland Happy Birthday with Cuddly Photo: He 'Makes Me the Happiest'

Zendaya Wishes Boyfriend Tom Holland Happy Birthday with Cuddly Photo: He 'Makes Me the Happiest'

Zendaya shared a sweet photo in honor of boyfriend Tom Holland's 26th birthday Wednesday

小さな女性:脳卒中を患った後に病院から解放されたアトランタのジューシーな赤ちゃん:「まだ癒し」

小さな女性:脳卒中を患った後に病院から解放されたアトランタのジューシーな赤ちゃん:「まだ癒し」

シーレン「Ms.JuicyBaby」ピアソンは、先月脳卒中で入院した後、「もう一度たくさんのことをする方法を学ばなければならない」ため、言語療法を受けていることを明らかにしました。

エマストーンは彼女のクリフサイドマリブビーチハウスを420万ドルでリストアップしています—中を見てください!

エマストーンは彼女のクリフサイドマリブビーチハウスを420万ドルでリストアップしています—中を見てください!

オスカー受賞者の世紀半ばの家には、3つのベッドルーム、2つのバス、オーシャンフロントの景色があります。

ジーニー・メイ・ジェンキンスは、母乳育児の経験の中で、彼女は「本当に、本当に落ち込んでいる」と言います

ジーニー・メイ・ジェンキンスは、母乳育児の経験の中で、彼女は「本当に、本当に落ち込んでいる」と言います

ジーニー・メイ・ジェンキンスは、生後4か月の娘、モナコに母乳育児をしていると語った。

投資ノート:Bioscout AU$300万シード

投資ノート:Bioscout AU$300万シード

Bioscoutは、農家を運転席に置くという使命を負っています。Artesian(GrainInnovate)やUniseedと並んで、最新のシードラウンドでチームを支援できることをうれしく思います。問題真菌症による重大な作物の損失は、農民にとって試練であることが証明されています。

リトルマーケットリサーチ1| 2022年のクイックグリンプス遠隔医療市場

リトルマーケットリサーチ1| 2022年のクイックグリンプス遠隔医療市場

遠隔医療は、パンデミック後の時代では新しいものではなく、時代遅れの分野でもありません。しかし、業界を詳しく見ると、需要と供給の強力な持続可能性と、米国で絶え間ない革命となる強力な潜在的成長曲線を示しています。

スタートアップ資金調達環境:タイのスタートアップエコシステムの次は何ですか?

スタートアップ資金調達環境:タイのスタートアップエコシステムの次は何ですか?

2021年は、世界的なベンチャーキャピタル(VC)の資金調達にとって記録的な年でした。DealStreetAsiaによると、東南アジアも例外ではなく、この地域では年間で記録的な25の新しいユニコーンが採掘されました。

ムーアの法則を超えて

ムーアの法則を超えて

計算に対する私たちの欲求とムーアの法則が提供できるものとの間には、指数関数的に増大するギャップがあります。私たちの文明は計算に基づいています—建築と想像力の現在の限界を超える技術を見つけなければなりません。

Language