JavaScriptの変数の範囲は何ですか?それらは関数の外側とは反対に内側で同じスコープを持っていますか?それとも重要なのですか?また、グローバルに定義されている場合、変数はどこに格納されますか?
私ができる最善のことは、勉強するためのたくさんの例をあげることだと思います。 Javascriptプログラマは実際には範囲をどの程度理解しているかによってランク付けされます。それは時々かなり直観的ではないかもしれません。
グローバルスコープの変数
// global scope
var a = 1;
function one() {
alert(a); // alerts '1'
}
ローカルスコープ
// global scope
var a = 1;
function two(a) { // passing (a) makes it local scope
alert(a); // alerts the given argument, not the global value of '1'
}
// local scope again
function three() {
var a = 3;
alert(a); // alerts '3'
}
中級 :JavaScriptのブロックスコープのようなものはありません(ES5; ES6では let
が導入されました)
a。
var a = 1;
function four() {
if (true) {
var a = 4;
}
alert(a); // alerts '4', not the global value of '1'
}
b。
var a = 1;
function one() {
if (true) {
let a = 4;
}
alert(a); // alerts '1' because the 'let' keyword uses block scoping
}
中級 :オブジェクトプロパティ
var a = 1;
function Five() {
this.a = 5;
}
alert(new Five().a); // alerts '5'
詳細 :終了
var a = 1;
var six = (function() {
var a = 6;
return function() {
// JavaScript "closure" means I have access to 'a' in here,
// because it is defined in the function in which I was defined.
alert(a); // alerts '6'
};
})();
上級 :プロトタイプベースのスコープ解決
var a = 1;
function seven() {
this.a = 7;
}
// [object].prototype.property loses to
// [object].property in the lookup chain. For example...
// Won't get reached, because 'a' is set in the constructor above.
seven.prototype.a = -1;
// Will get reached, even though 'b' is NOT set in the constructor.
seven.prototype.b = 8;
alert(new seven().a); // alerts '7'
alert(new seven().b); // alerts '8'
グローバル+ローカル :さらに複雑な場合
var x = 5;
(function () {
console.log(x);
var x = 10;
console.log(x);
})();
JavaScriptは常に変数宣言(初期化ではない)をスコープの先頭に移動するので、これは10
および5
ではなくundefined
および10
を出力します。コードは次のようになります。
var x = 5;
(function () {
var x;
console.log(x);
x = 10;
console.log(x);
})();
キャッチ句スコープ変数
var e = 5;
console.log(e);
try {
throw 6;
} catch (e) {
console.log(e);
}
console.log(e);
これは5
、6
、5
を出力します。 catch節の中e
はグローバル変数とローカル変数を隠します。しかし、この特別な範囲は捕捉された変数のためだけのものです。 catch句の中にvar f;
を書くと、try-catchブロックの前後に定義したのとまったく同じになります。
Javascriptはスコープチェーンを使用して特定の機能のスコープを確立します。通常は1つのグローバルスコープがあり、定義された各関数には独自のネストスコープがあります。他の関数内で定義された関数はすべて、外部関数にリンクされているローカルスコープを持ちます。スコープを定義するのは、常にソース内の位置です。
スコープチェーン内の要素は基本的に、その親スコープへのポインタを持つMapです。
変数を解決するとき、javascriptは最も内側のスコープから開始して外側に検索します。
伝統的に、JavaScriptは実際には2種類のスコープしかありません。
違いを説明する他の多くの答えがすでにあるので、私はこれについては詳述しません。
最新の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コードを書いていて、ブラウザベースのtranspiler( Traceur または babel-standalone のように)を使っているなら、安全に使えます。 let
ステートメント、しかしあなたのコードはパフォーマンスに関して最適以外のものになるでしょう。
クライアントサイドのJavaScriptコードを書いていて、Nodeベースのtranspiler( traceurシェルスクリプト または Babel のような)を使っているなら、安全に使えます。 let
ステートメントそして、あなたのブラウザは変換されたコードだけを知っているので、パフォーマンスの欠点は制限されるべきです。
クライアントサイドのJavaScriptコードを書いていてトランスパイラーを使用していない場合は、ブラウザのサポートを考慮する必要があります。
これらはlet
をまったくサポートしていないブラウザです。
この回答を読んだ時点でどのブラウザがlet
ステートメントをサポートしているかについての最新の概要は、 このCan I Use
ページ を参照してください。
(*)JavaScript変数は hoisted であるため、グローバルスコープおよび機能スコープの変数は宣言前に初期化して使用できます。これは宣言が常にスコープの最上位にあることを意味します。
(**)ブロックスコープ変数は持ち上げられません
これが例です:
<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
Brendan Eich によって最初に設計されたときのJavaScriptでのスコープの考えは HyperCard スクリプト言語 HyperTalk から来ました。
この言語では、表示はインデックスカードのスタックと同様に行われました。背景と呼ばれるマスターカードがありました。それは透明で、一番下のカードとして見ることができます。このベースカードの内容は、その上に置かれているカードと共有されていました。一番上に置かれた各カードは、前のカードよりも優先される独自のコンテンツを持っていましたが、必要に応じて前のカードにアクセスできました。
これがまさにJavaScriptスコープシステムの設計方法です。名前が違うだけです。 JavaScriptのカードはとして知られています実行コンテキストECMA 。これらの各コンテキストには、3つの主要部分があります。可変環境、字句環境、およびこのバインディング。カードのリファレンスに戻ると、字句環境には、スタックの下位にある以前のカードのすべてのコンテンツが含まれています。現在のコンテキストはスタックの最上位にあり、そこで宣言されたコンテンツはすべて変数環境に格納されます。命名の衝突の場合は、可変環境が優先されます。
Thisバインディングは、それを含むオブジェクトを指します。包含オブジェクトがwindow
またはコンストラクタ関数である可能性がある宣言済み関数のように、スコープまたは実行コンテキストが包含オブジェクトを変更せずに変更されることがあります。
これらの実行コンテキストは、制御が転送されるたびに作成されます。コードの実行開始時に制御が移され、これは主に関数の実行から行われます。
これが技術的な説明です。実際には、JavaScriptでそれを覚えておくことが重要です
このページの前の例(5. "クロージャ")の1つにこれを適用すると、実行コンテキストのスタックに従うことができます。この例では、スタック内に3つのコンテキストがあります。それらは、外側の文脈、var sixによって呼び出されたすぐに呼び出された関数内の文脈、およびvar 6のすぐに呼び出された関数内の返された関数内の文脈によって定義されます。
i)外側のコンテキスト。 a = 1の可変環境です。
ii)IIFEコンテキストでは、a = 1の字句環境がありますが、スタックで優先されるa = 6の可変環境があります。
iii)返された関数コンテキスト。これはa = 6の字句環境を持ち、それは呼び出されたときにアラートで参照される値です。
1)グローバルスコープ、関数スコープ、withスコープとcatchスコープがあります。変数の一般的な 'ブロック'レベルのスコープはありません - withとcatchステートメントはそれらのブロックに名前を追加します。
2)スコープはグローバルスコープに至るまで関数によってネストされています。
3)プロパティはプロトタイプチェーンをたどることによって解決されます。 with文は、オブジェクトプロパティ名をwithブロックで定義されている字句スコープに入れます。
編集:ECMAAScript 6(ハーモニー)はletをサポートするように指定されています、そして私はchromeが 'ハーモニー'フラグを許可することを知っているので、おそらくそれはそれをサポートします..
ブロックレベルのスコープをサポートするようにしましょうが、それを実現するにはキーワードを使用する必要があります。
編集:コメントの中で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です。すでに定義されている名前に少し頭を向けると、これははるかに安全になります。私は個人的にこれのために決して使わないでしょう。
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
が最初に最も近い変数スコープを検索し、variableがそこにない場合、それは変数スコープチェーンの次のオブジェクトに移動します。
他の答えに加えて、scopeは宣言されたすべての識別子(変数)のルックアップリストであり、これらが現在実行中のコードにどのようにアクセス可能かについての一連の厳密な規則を強制します。この検索は、LHS(左側)参照である変数への代入を目的としている場合もあれば、RHS(右側)参照である値の取得を目的としている場合もあります。これらのルックアップは、コードをコンパイルして実行するときにJavaScriptエンジンが内部的に行っていることです。
この観点から、私はKyle SimpsonのScopes and Closures電子ブックで私が見つけた写真が役に立つと思う。
彼の電子ブックからの引用:
建物は、プログラムのネストされたスコープルールセットを表します。建物の1階は、どこにいても、現在実行中のスコープを表します。建物の最上位レベルはグローバルスコープです。 LHSおよびRHSの参照を解決するには、現在の階を調べ、それが見つからない場合は、エレベーターを次の階に移動し、そこを見て、次に次のようにします。最上階(グローバルスコープ)にたどり着くと、探しているものが見つかるか、いないかのどちらかになります。しかし、あなたは無関係にやめなければなりません。
「スコープの検索は、最初の一致が見つかると停止します」という1つの注目すべき点があります。
この「スコープレベル」の考え方は、ネストされた関数で検索されている場合に、なぜこれが新しく作成されたスコープで変更されるのかを説明します。ここにこれらの詳細すべてに入るリンクがあります、 あなたがjavascriptの範囲について知りたいことすべて
グローバル変数はグローバルスターとまったく同じです(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
コードを実行してください。これがスコープについての考えを与えることを願っています
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);
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番目の例では、関数による新しいスコープがあり、最初に宣言された変数はSHADOWEDで上書きされませんでした。
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%の場合に使用されるべきです 。変数 reference が変更できないようにするためです。配列、オブジェクト、および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
この好奇心旺盛な例を試してください。以下の例では、aが0で初期化された数値ならば、0と1が表示されます。その結果、両方とも同じアラートが表示されます。
var a = new Date();
function f1(b)
{
b.setDate(b.getDate()+1);
alert(b.getDate());
}
f1(a);
alert(a.getDate());
JavaScriptには2種類のスコープがあります。
Below関数はローカルスコープ変数carName
を持ちます。そしてこの変数は関数の外側からはアクセスできない。
function myFunction() {
var carName = "Volvo";
alert(carName);
// code here can use carName
}
Belowクラスには、グローバルスコープ変数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の値は文字列innerFunc
に解決されます。innerFunc
自体の中に見つかりません。したがって、 スコープチェーン を登る必要があります。最初に、関数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ブロックの外側にアクセスすることはできません。
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
EcmaScript 5には主に2つのスコープ、 local scope および global scope がありますが、EcmaScript 6には主に3つのスコープ、ローカルスコープ、グローバルスコープ、および block scope という新しいスコープがあります。
ブロックの有効範囲の例は次のとおりです。
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.
}