ECMAScript 6では let
文が導入されました 。
「ローカル」変数として記述されていると聞いたことがありますが、それがvar
キーワードとは異なる動作をするかどうかはまだよくわかりません。
違いは何ですか? let
をvar
の上でいつ使用すべきですか?
違いはスコープです。 var
は最も近いファンクションブロックにスコープ指定され、let
は最も近いエンクロージングブロックにスコープ指定されます。いずれかのブロックの外側にある場合、両方ともグローバルです。
また、let
で宣言された変数は、それを囲むブロックで宣言されるまではアクセスできません。デモで見られるように、これはReferenceError例外を投げます。
デモ :
var html = '';
write('#### global ####\n');
write('globalVar: ' + globalVar); //undefined, but visible
try {
write('globalLet: ' + globalLet); //undefined, *not* visible
} catch (exception) {
write('globalLet: exception');
}
write('\nset variables');
var globalVar = 'globalVar';
let globalLet = 'globalLet';
write('\nglobalVar: ' + globalVar);
write('globalLet: ' + globalLet);
function functionScoped() {
write('\n#### function ####');
write('\nfunctionVar: ' + functionVar); //undefined, but visible
try {
write('functionLet: ' + functionLet); //undefined, *not* visible
} catch (exception) {
write('functionLet: exception');
}
write('\nset variables');
var functionVar = 'functionVar';
let functionLet = 'functionLet';
write('\nfunctionVar: ' + functionVar);
write('functionLet: ' + functionLet);
}
function blockScoped() {
write('\n#### block ####');
write('\nblockVar: ' + blockVar); //undefined, but visible
try {
write('blockLet: ' + blockLet); //undefined, *not* visible
} catch (exception) {
write('blockLet: exception');
}
for (var blockVar = 'blockVar', blockIndex = 0; blockIndex < 1; blockIndex++) {
write('\nblockVar: ' + blockVar); // visible here and whole function
};
for (let blockLet = 'blockLet', letIndex = 0; letIndex < 1; letIndex++) {
write('blockLet: ' + blockLet); // visible only here
};
write('\nblockVar: ' + blockVar);
try {
write('blockLet: ' + blockLet); //undefined, *not* visible
} catch (exception) {
write('blockLet: exception');
}
}
function write(line) {
html += (line ? line : '') + '<br />';
}
functionScoped();
blockScoped();
document.getElementById('results').innerHTML = html;
<pre id="results"></pre>
ファンクションブロックの外側でこのように使用すると、非常によく似ています。
let me = 'go'; // globally scoped
var i = 'able'; // globally scoped
ただし、let
で定義されたグローバル変数は、window
で定義されたもののようにグローバルvar
オブジェクトのプロパティとして追加されません。
console.log(window.me); // undefined
console.log(window.i); // 'able'
ファンクションブロックでこのように使用した場合、それらは同一です。
function ingWithinEstablishedParameters() {
let terOfRecommendation = 'awesome worker!'; //function block scoped
var sityCheerleading = 'go!'; //function block scoped
}
これが違いです。 let
はfor()
ループ内でのみ表示され、var
は関数全体から認識されます。
function allyIlliterate() {
//tuce is *not* visible out here
for( let tuce = 0; tuce < 5; tuce++ ) {
//tuce is only visible in here (and in the for() parentheses)
//and there is a separate tuce variable for each iteration of the loop
}
//tuce is *not* visible out here
}
function byE40() {
//nish *is* visible out here
for( var nish = 0; nish < 5; nish++ ) {
//nish is visible to the whole function
}
//nish *is* visible out here
}
厳密モードを想定すると、var
を使用すると、同じスコープ内で同じ変数を再宣言できます。一方、let
は以下のことをしません。
'use strict';
let me = 'foo';
let me = 'bar'; // SyntaxError: Identifier 'me' has already been declared
'use strict';
var me = 'foo';
var me = 'bar'; // No problem, `me` is replaced.
let
はクロージャーに関する問題を避けるためにも使うことができます。以下の例に示すように、古い参照を保持するのではなく、新しい値をバインドします。
for(var i = 1; i < 6; i++) {
document.getElementById('my-element' + i)
.addEventListener('click', function() { alert(i) })
}
上記のコードは、古典的なJavaScriptのクロージャ問題を示しています。 i
の実際の値ではなく、i
変数への参照がクリックハンドラクロージャに格納されています。
1つのクリックハンドラは同じオブジェクトを参照します。6を保持するカウンタオブジェクトは1つだけなので、各クリックで6つ取得されます。
一般的な回避策は、これを無名関数でラップし、引数としてi
を渡すことです。このような問題は、以下のコードに示すようにlet
の代わりにvar
を使用することで回避することもできます。
_ demo _ (ChromeおよびFirefox 50でテスト済み)
'use strict';
for(let i = 1; i < 6; i++) {
document.getElementById('my-element' + i)
.addEventListener('click', function() { alert(i) })
}
let
とvar
の違いは何ですか?var
ステートメントを使用して定義された変数は、 thefunction 関数全体の最初から定義されています。*let
ステートメントを使用して定義された変数は、 the block でのみ認識されます。 。**違いを理解するには、次のコードを検討してください。
// 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
は関数全体で既知です。
また、ブロックスコープの変数は、ホイストされていないため、宣言される前は不明であると考えてください。また、同じブロック内で同じブロックスコープ変数を再宣言することもできません。これにより、ブロックスコープ変数は、グローバルスコープまたは機能スコープ変数よりもエラーが発生しにくくなります。これらの変数は、ホイストされ、複数の宣言の場合にエラーを生成しません。
let
を使用しても安全ですか?将来的にはletステートメントのみを使用し、varステートメントは廃止されると主張する人もいます。 JavaScriptの第一人者 Kyle Simpson 書いた 彼の理由に関する非常に精巧な記事そうではないと考えています 。
しかし、今日では、そうではありません。実際、let
ステートメントを使用しても安全かどうかを実際に確認する必要があります。その質問に対する答えは、環境によって異なります。
サーバー側のJavaScriptコード( Node.js )を記述している場合は、let
ステートメントを安全に使用できます。
クライアント側のJavaScriptコードを記述しており、ブラウザベースのトランスパイラーを使用している場合( Traceur または babel-standalone )、let
ステートメントを安全に使用できますが、コードはパフォーマンスに関して最適ではない可能性があります。
クライアント側のJavaScriptコードを記述しており、Nodeベースのトランスパイラーを使用している場合( traceur Shell script または Babel )、let
ステートメントを安全に使用できます。また、ブラウザはトランスコードされたコードのみを認識するため、パフォーマンスの欠点を制限する必要があります。
クライアント側のJavaScriptコードを記述していて、トランスパイラーを使用しない場合は、ブラウザーのサポートを検討する必要があります。
let
をまったくサポートしないブラウザーがまだあります:
この回答を読んだ時点でどのブラウザがlet
ステートメントをサポートしているかの最新の概要については、 this Can I Use
page 。
* JavaScript変数は hoisted であるため、グローバルおよび機能スコープの変数は、宣言される前に初期化および使用できます=。これは、宣言が常にスコープの最上位にあることを意味します。
** ブロックスコープ変数はホイストされません
これは let
キーワードの説明です いくつかの例を示します。
varと非常によく似た動作をさせてください。主な違いは、var変数の有効範囲はそれを囲む関数全体であるということです。
この表 ウィキペディアの/は、どのブラウザがJavascript 1.7をサポートしているかを示しています。
MozillaとChromeブラウザだけがそれをサポートしていることに注意してください。 IE、Safari、そして潜在的に他の人はそうではありません。
受け入れられた答えはポイントを逃しています:
{
let a = 123;
};
console.log(a); // ReferenceError: a is not defined
let
let
キーワードを使用して宣言された変数はブロックスコープです。つまり、それらは宣言された block でのみ使用できます。
最上位レベルでは、let
を使用して宣言された変数はグローバルオブジェクトにプロパティを作成しません。
var globalVariable = 42;
let blockScopedVariable = 43;
console.log(globalVariable); // 42
console.log(blockScopedVariable); // 43
console.log(this.globalVariable); // 42
console.log(this.blockScopedVariable); // undefined
関数の内部(ただしブロックの外部)では、let
はvar
と同じ有効範囲を持ちます。
(() => {
var functionScopedVariable = 42;
let blockScopedVariable = 43;
console.log(functionScopedVariable); // 42
console.log(blockScopedVariable); // 43
})();
console.log(functionScopedVariable); // ReferenceError: functionScopedVariable is not defined
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined
ブロック内でlet
を使用して宣言された変数は、そのブロック外ではアクセスできません。
{
var globalVariable = 42;
let blockScopedVariable = 43;
console.log(globalVariable); // 42
console.log(blockScopedVariable); // 43
}
console.log(globalVariable); // 42
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined
ループ内でlet
で宣言された変数は、そのループ内でのみ参照できます。
for (var i = 0; i < 3; i++) {
var j = i * 2;
}
console.log(i); // 3
console.log(j); // 4
for (let k = 0; k < 3; k++) {
let l = k * 2;
}
console.log(typeof k); // undefined
console.log(typeof l); // undefined
// Trying to do console.log(k) or console.log(l) here would throw a ReferenceError.
ループ内でlet
の代わりにvar
を使用すると、反復ごとに新しい変数が得られます。つまり、ループ内でクロージャを安全に使用できるということです。
// Logs 3 thrice, not what we meant.
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
// Logs 0, 1 and 2, as expected.
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 0);
}
一時的なデッドゾーン のため、let
を使用して宣言された変数は、宣言される前にアクセスできません。実行しようとするとエラーが発生します。
console.log(noTDZ); // undefined
var noTDZ = 43;
console.log(hasTDZ); // ReferenceError: hasTDZ is not defined
let hasTDZ = 42;
let
を使用して同じ変数を複数回宣言することはできません。 let
を使って宣言された別の変数と同じ識別子を持つvar
を使って変数を宣言することもできません。
var a;
var a; // Works fine.
let b;
let b; // SyntaxError: Identifier 'b' has already been declared
var c;
let c; // SyntaxError: Identifier 'c' has already been declared
const
const
はlet
と非常によく似ています - ブロックスコープでTDZがあります。ただし、違いが2つあります。
const
を使用して宣言された変数は再割り当てできません。
const a = 42;
a = 43; // TypeError: Assignment to constant variable.
値が不変であるという意味ではありません。その特性はまだ変更することができます。
const obj = {};
obj.a = 42;
console.log(obj.a); // 42
不変オブジェクトを使いたい場合は、 Object.freeze()
を使用してください。
const
を使用して変数を宣言するときは、常に値を指定する必要があります。
const a; // SyntaxError: Missing initializer in const declaration
これは両者の違いの例です(サポートはchromeから始まったばかりです):
ご覧のとおり、var j
変数はまだforループの有効範囲外(Block Scope)の値を持ちますが、let i
変数はforループの有効範囲外では未定義です。
"use strict";
console.log("var:");
for (var j = 0; j < 2; j++) {
console.log(j);
}
console.log(j);
console.log("let:");
for (let i = 0; i < 2; i++) {
console.log(i);
}
console.log(i);
微妙な違いがいくつかあります - let
スコープは、他の言語の変数スコープとほとんど同じように動作します。
例えばそれは囲んでいるブロックまで及んでいます、それらは宣言される前に存在しません、など。
しかしlet
は新しいJavascriptの実装の一部にすぎず、さまざまな程度の ブラウザサポート を持っていることに注目する価値があります。
主な違いはscopeの違いですが、letのみ利用可能ですforループのように、scopeスコープ内で、varたとえば、ループの外側にアクセスします。 MDN のドキュメントから(例もMDNから):
letを使用すると、スコープが使用されるブロック、ステートメント、または式に限定された変数を宣言できます。これは、varキーワードとは異なり、ブロックスコープに関係なく、関数全体に対して変数をグローバルに、またはローカルに定義します。
letで宣言された変数のスコープは、変数が定義されているブロックと、含まれているサブブロックにあります。このように、letはvarと非常によく似ています。主な違いは、var変数のスコープが囲み関数全体であることです:
function varTest() {
var x = 1;
if (true) {
var x = 2; // same variable!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
if (true) {
let x = 2; // different variable
console.log(x); // 2
}
console.log(x); // 1
}`
プログラムと関数の最上位レベルでは、let、varとは異なり、グローバルオブジェクトにプロパティを作成しません。例えば:
var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined
ブロック内で使用する場合、変数のスコープをそのブロックに制限します。スコープが宣言されている関数内にあるvarの違いに注意してください。
var a = 1;
var b = 2;
if (a === 1) {
var a = 11; // the scope is global
let b = 22; // the scope is inside the if-block
console.log(a); // 11
console.log(b); // 22
}
console.log(a); // 11
console.log(b); // 2
また、ECMA6機能であることも忘れないでください。まだ完全にはサポートされていないので、Babelなどを使用してECMA5にトランスパイルすることをお勧めします。.. babel website
吊りない変数
let
は、出現するブロックの有効範囲全体に対して、 ホイストではなく になります。対照的に、var
は、次のようにホイストできます。
{
console.log(cc); // undefined. Caused by hoisting
var cc = 23;
}
{
console.log(bb); // ReferenceError: bb is not defined
let bb = 23;
}
実はPer @Bergi、 var
とlet
の両方がホイストされています 。
ガベージコレクション
let
のブロックスコープは、メモリを再利用するためのクロージャとガベージコレクションに関連して役立ちます。考えて、
function process(data) {
//...
}
var hugeData = { .. };
process(hugeData);
var btn = document.getElementById("mybutton");
btn.addEventListener( "click", function click(evt){
//....
});
click
ハンドラーコールバックは、hugeData
変数をまったく必要としません。理論的には、process(..)
の実行後、巨大なデータ構造hugeData
はガベージコレクションされる可能性があります。ただし、click
関数はスコープ全体を閉じるため、一部のJSエンジンではまだこの巨大な構造を維持する必要がある可能性があります。
しかし、ブロックスコープはこの巨大なデータ構造をガベージコレクションにすることができます。
function process(data) {
//...
}
{ // anything declared inside this block can be garbage collected
let hugeData = { .. };
process(hugeData);
}
var btn = document.getElementById("mybutton");
btn.addEventListener( "click", function click(evt){
//....
});
let
ループ
ループ内のlet
は、 ループの各反復に を再バインドして、前のループ反復の最後から必ず値を割り当て直すことができます。考えて、
// print '5' 5 times
for (var i = 0; i < 5; ++i) {
setTimeout(function () {
console.log(i);
}, 1000);
}
ただし、var
をlet
に置き換えます。
// print 1, 2, 3, 4, 5. now
for (let i = 0; i < 5; ++i) {
setTimeout(function () {
console.log(i);
}, 1000);
}
let
は、a)イニシャライザ式b)各反復(以前の増分式の評価)の名前で新しい字句環境を作成するため、詳細は here です。
これは他の人がすでに書いたことに追加する例です。関数の配列adderFunctions
を作成したいとしましょう。ここで、各関数は単一のNumber引数を取り、配列内の引数と関数のインデックスの合計を返します。 adderFunctions
キーワードを使用してループでvar
を生成しようとしても、誰かが単純に期待するような方法ではうまくいきません。
// An array of adder functions.
var adderFunctions = [];
for (var i = 0; i < 1000; i++) {
// We want the function at index i to add the index to its argument.
adderFunctions[i] = function(x) {
// What is i bound to here?
return x + i;
};
}
var add12 = adderFunctions[12];
// Uh oh. The function is bound to i in the outer scope, which is currently 1000.
console.log(add12(8) === 20); // => false
console.log(add12(8) === 1008); // => true
console.log(i); // => 1000
// It gets worse.
i = -8;
console.log(add12(8) === 0); // => true
i
のスコープは、各関数が作成されたfor
ブロックの繰り返しを超えて拡張されるため、上記のプロセスでは目的の関数の配列は生成されません。代わりに、ループの最後では、各関数のクロージャのi
は、i
内のすべての無名関数について、ループの最後のadderFunctions
の値(1000)を参照します。これは私たちがまったく望んでいたことではありません。私たちは今、全く同じ振る舞いを持つ1000の異なる関数の配列をメモリに持っています。そしてその後にi
の値を更新すると、その突然変異はすべてのadderFunctions
に影響します。
ただし、let
キーワードを使用してもう一度試すことができます。
// Let's try this again.
// NOTE: We're using another ES6 keyword, const, for values that won't
// be reassigned. const and let have similar scoping behavior.
const adderFunctions = [];
for (let i = 0; i < 1000; i++) {
// NOTE: We're using the newer arrow function syntax this time, but
// using the "function(x) { ..." syntax from the previous example
// here would not change the behavior shown.
adderFunctions[i] = x => x + i;
}
const add12 = adderFunctions[12];
// Yay! The behavior is as expected.
console.log(add12(8) === 20); // => true
// i's scope doesn't extend outside the for loop.
console.log(i); // => ReferenceError: i is not defined
今回は、i
はfor
ループの各反復でリバウンドされます。各関数は関数の作成時にi
の値を保持するようになり、adderFunctions
は期待通りに動作します。
さて、2つの振る舞いをイメージミキシングすると、おそらく同じスクリプト内で新しいlet
とconst
を古いvar
と混合することが推奨されない理由がわかります。これを行うと、非常に混乱しやすいコードになります。
const doubleAdderFunctions = [];
for (var i = 0; i < 1000; i++) {
const j = i;
doubleAdderFunctions[i] = x => x + i + j;
}
const add18 = doubleAdderFunctions[9];
const add24 = doubleAdderFunctions[12];
// It's not fun debugging situations like this, especially when the
// code is more complex than in this example.
console.log(add18(24) === 42); // => false
console.log(add24(18) === 42); // => false
console.log(add18(24) === add24(18)); // => false
console.log(add18(24) === 2018); // => false
console.log(add24(18) === 2018); // => false
console.log(add18(24) === 1033); // => true
console.log(add24(18) === 1030); // => true
これをあなたに起こさせないでください。リンターを使用してください。
注: これはループ内での
var
/let
の動作を説明することを目的とした教育例で、これも理解しやすいものです。これは数字を追加するためのひどい方法でしょう。しかし、無名関数クロージャでデータをキャプチャする一般的な手法は、他の状況では現実の世界で見られるかもしれません。 YMMV.
違いは、それぞれで宣言された変数の scope にあります。
実際には、範囲の違いにより多くの有用な結果があります。
let
変数は、最近接囲みブロック({ ... }
)でのみ表示されます。let
変数は、発生するコード行でのみ使用できますafter変数は宣言されます( それらが持ち上げられます !)。let
変数は、後続のvar
またはlet
で再宣言することはできません。let
変数は、グローバルwindow
オブジェクトに追加されません。let
変数は、クロージャーで使いやすいです(これらは 競合状態 を引き起こしません)。let
によって課される制限により、変数の可視性が低下し、予期しない名前の衝突が早期に見つかる可能性が高くなります。これにより、 reachability (未使用メモリの再利用の支援)など、変数の追跡と推論が容易になります。
したがって、let
変数は、大規模なプログラムで使用したり、独自に開発したフレームワークを新しい予期しない方法で組み合わせたりした場合に問題を引き起こす可能性が低くなります。
var
は、ループ内でクロージャーを使用する場合(#5)またはコードで外部から見えるグローバル変数を宣言する場合(#4)にシングルバインディング効果が必要なことが確実な場合に役立ちます。 var
がトランスパイラー空間からコア言語に移行する場合、エクスポートにexport
を使用することはできません。
1。最寄りの囲みブロックの外側で使用しない:x
の2番目の使用は、let
で宣言されたブロックの外側で発生するため、このコードブロックは参照エラーをスローします。
{
let x = 1;
}
console.log(`x is ${x}`); // ReferenceError during parsing: "x is not defined".
対照的に、var
を使用した同じ例が機能します。
2。宣言の前に使用しない:
このコードブロックは、ReferenceError
が宣言される前に使用されるため、コードを実行する前にx
をスローします。
{
x = x + 1; // ReferenceError during parsing: "x is not defined".
let x;
console.log(`x is ${x}`); // Never runs.
}
対照的に、var
を使用した同じ例は、例外をスローすることなく解析および実行されます。
3。再宣言なし:次のコードは、let
で宣言された変数は後で再宣言できないことを示しています。
let x = 1;
let x = 2; // SyntaxError: Identifier 'x' has already been declared
4。 window
に接続されていないグローバル:
var button = "I cause accidents because my name is too common.";
let link = "Though my name is common, I am harder to access from other JS files.";
console.log(link); // OK
console.log(window.link); // undefined (GOOD!)
console.log(window.button); // OK
5。クロージャーでの簡単な使用:var
で宣言された変数は、ループ内のクロージャーではうまく機能しません。変数i
がさまざまな時点で持つ値のシーケンスを出力する単純なループを次に示します。
for (let i = 0; i < 5; i++) {
console.log(`i is ${i}`), 125/*ms*/);
}
具体的には、次の出力があります。
i is 0
i is 1
i is 2
i is 3
i is 4
JavaScriptでは、変数が作成されたときよりもかなり後で変数を使用することがよくあります。 setTimeout
に渡されたクロージャーで出力を遅らせることでこれを実証する場合:
for (let i = 0; i < 5; i++) {
setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}
... let
を使用している限り、出力は変更されません。対照的に、代わりにvar i
を使用した場合:
for (var i = 0; i < 5; i++) {
setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}
...ループは予期せず「i is 5」を5回出力します。
i is 5
i is 5
i is 5
i is 5
i is 5
var
とlet
の主な違いは、var
で宣言された変数がfunction scopedであることです。 let
で宣言された関数はblock scopedです。例えば:
function testVar () {
if(true) {
var foo = 'foo';
}
console.log(foo);
}
testVar();
// logs 'foo'
function testLet () {
if(true) {
let bar = 'bar';
}
console.log(bar);
}
testLet();
// reference error
// bar is scoped to the block of the if statement
var
を持つ変数:
最初の関数testVar
がvar
で宣言された変数fooを呼び出すと、if
ステートメントの外部からアクセスできます。この変数foo
は、testVar
のスコープ内でeverywhereが使用可能になりますfunction。
let
を持つ変数:
2番目の関数testLet
がlet
で宣言された変数barを呼び出すと、if
ステートメント内でのみアクセスできます。 let
で宣言された変数はブロックスコープであるため(ブロックは中括弧で囲まれたコードです(例:if{}
、for{}
、function{}
)。
let
変数は引き上げられません:var
とlet
のもう1つの違いは、let
で宣言された変数です上げないでください。例は、この動作を説明する最良の方法です。
let
の変数do n't上げられます:
console.log(letVar);
let letVar = 10;
// referenceError, the variable doesn't get hoisted
var
の変数do巻き上げられます:
console.log(varVar);
var varVar = 10;
// logs undefined, the variable gets hoisted
let
はwindow
にアタッチされません:グローバルスコープ(関数内にないコード)でlet
で宣言された変数は、グローバルwindow
オブジェクトのプロパティとして追加されません。例(このコードはグローバルスコープ内にあります):
var bar = 5;
let foo = 10;
console.log(bar); // logs 5
console.log(foo); // logs 10
console.log(window.bar);
// logs 5, variable added to window object
console.log(window.foo);
// logs undefined, variable not added to window object
let
でvar
を使用する必要がある場合
スコープがより限定されているため、できる限り、let
ではなくvar
を使用してください。これにより、多数の変数を処理するときに発生する可能性のある名前の競合が減少します。 var
は、グローバル変数をwindow
オブジェクトに明示的に含める場合に使用できます(これが本当に必要な場合は、常に慎重に検討してください)。
次の2つの機能が違いを示していますか。
function varTest() {
var x = 31;
if (true) {
var x = 71; // Same variable!
console.log(x); // 71
}
console.log(x); // 71
}
function letTest() {
let x = 31;
if (true) {
let x = 71; // Different variable
console.log(x); // 71
}
console.log(x); // 31
}
let
は興味深いものです。
(() => {
var count = 0;
for (let i = 0; i < 2; ++i) {
for (let i = 0; i < 2; ++i) {
for (let i = 0; i < 2; ++i) {
console.log(count++);
}
}
}
})();
これは[0、7]を数えることになります。
一方で
(() => {
var count = 0;
for (var i = 0; i < 2; ++i) {
for (var i = 0; i < 2; ++i) {
for (var i = 0; i < 2; ++i) {
console.log(count++);
}
}
}
})();
[0、1]だけをカウントします。
仕様を正しく読んだ場合、let
thankfullyも活用して、 自己呼び出し関数 をシミュレートするプライベートのみのメンバー-コードの可読性を低下させ、デバッグを複雑にし、実際のコード保護やその他の利点を追加しない人気のあるデザインパターン-セマンティクスに対する誰かの欲求を満たすことができる場合を除き、使用をやめる/ rant
var SomeConstructor;
{
let privateScope = {};
SomeConstructor = function SomeConstructor () {
this.someProperty = "foo";
privateScope.hiddenProperty = "bar";
}
SomeConstructor.prototype.showPublic = function () {
console.log(this.someProperty); // foo
}
SomeConstructor.prototype.showPrivate = function () {
console.log(privateScope.hiddenProperty); // bar
}
}
var myInstance = new SomeConstructor();
myInstance.showPublic();
myInstance.showPrivate();
console.log(privateScope.hiddenProperty); // error
「 プライベートインターフェイスのエミュレート 」を参照してください
また、少なくともVisual Studio 2015のTypeScript 1.5では、 "var"はブロック内で同じ変数名の複数の宣言を許可し、 "let"は許可しません。
これによってコンパイルエラーが発生することはありません。
var x = 1;
var x = 2;
この意志:
let x = 1;
let x = 2;
var
はグローバルスコープ(ホイスト可能)変数です。
let
とconst
はブロックスコープです。
test.js
{
let l = 'let';
const c = 'const';
var v = 'var';
v2 = 'var 2';
}
console.log(v, this.v);
console.log(v2, this.v2);
console.log(l); // ReferenceError: l is not defined
console.log(c); // ReferenceError: c is not defined
let
を使用する場合
let
キーワードは、それが含まれるブロック(通常は{ .. }
ペア)のスコープに変数宣言を関連付けます。言い換えると、let
は暗黙的にそのブロックのスコープを変数宣言のためにハイジャックします。
let
変数は、グローバルにアクセスできないため、window
オブジェクト内でアクセスすることはできません。
function a(){
{ // this is the Max Scope for let variable
let x = 12;
}
console.log(x);
}
a(); // Uncaught ReferenceError: x is not defined
var
を使用する場合
ES5のvar
および変数は、関数内にスコープを持ち、変数は関数内では有効であり、関数自体の外部では無効です。
var
変数はグローバルにアクセスできないため、window
オブジェクト内でアクセスできます。
function a(){ // this is the Max Scope for var variable
{
var x = 12;
}
console.log(x);
}
a(); // 12
もっと知りたいなら、以下を読み続けてください
範囲に関する最も有名なインタビューの質問の1つはまた下記のようにlet
とvar
の正確な使用で十分です。
let
を使用する場合
for (let i = 0; i < 10 ; i++) {
setTimeout(
function a() {
console.log(i); //print 0 to 9, that is literally AWW!!!
},
100 * i);
}
これは、let
を使用すると、ループの反復ごとに変数がスコープされ、独自のコピーを持つためです。
var
を使用する場合
for (var i = 0; i < 10 ; i++) {
setTimeout(
function a() {
console.log(i); //print 10 times 10
},
100 * i);
}
これは、var
を使用すると、ループの反復ごとに変数がスコープ指定され、コピーが共有されるためです。
let
を使ったハック:
1。
let statistics = [16, 170, 10];
let [age, height, grade] = statistics;
console.log(height)
2。
let x = 120,
y = 12;
[x, y] = [y, x];
console.log(`x: ${x} y: ${y}`);
3。
let node = {
type: "Identifier",
name: "foo"
};
let { type, name, value } = node;
console.log(type); // "Identifier"
console.log(name); // "foo"
console.log(value); // undefined
let node = {
type: "Identifier"
};
let { type: localType, name: localName = "bar" } = node;
console.log(localType); // "Identifier"
console.log(localName); // "bar"
let
を使ったゲッターとセッター:let jar = {
numberOfCookies: 10,
get cookies() {
return this.numberOfCookies;
},
set cookies(value) {
this.numberOfCookies = value;
}
};
console.log(jar.cookies)
jar.cookies = 7;
console.log(jar.cookies)
letはes6の一部です。これらの関数は違いを簡単に説明します。
function varTest() {
var x = 1;
if (true) {
var x = 2; // same variable!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
if (true) {
let x = 2; // different variable
console.log(x); // 2
}
console.log(x); // 1
}
これまでJavaScriptには機能的なものとグローバルなものの2つのスコープしかありませんでした。 'let
'キーワードにより、JavaScriptはblock-level
変数を導入しました。
'let'キーワードを完全に理解するには、ES6:JavaScriptで変数を宣言するための 'let'キーワードが役に立ちます。
この記事では、var、let、constの違いを明確に定義しています
const
は、識別子が再割り当てされないことを示すシグナルです。
let
は、ループ内のカウンタ、またはアルゴリズム内の値スワップなど、変数を再割り当てできることを示すシグナルです。また、で定義されているブロック内でのみ変数が使用されることを知らせるというシグナルも含まれます。これは必ずしも包含関数全体ではありません。JavaScriptで変数を定義した場合、
var
が最も弱いシグナルになりました。変数は再割り当てされてもされなくてもよく、変数は関数全体に使用されてもされなくてもよく、または単にブロックまたはループの目的に使用されてもされなくてもよい。
https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b
let
を使用して、ステートメントのブロックに対して変数の有効範囲を設定した方がよいと思います。
function printnums()
{
// i is not accessible here
for(let i = 0; i <10; i+=)
{
console.log(i);
}
// i is not accessible here
// j is accessible here
for(var j = 0; j <10; j++)
{
console.log(j);
}
// j is accessible here
}
他の言語、Java、C#などと同じようにJavaScriptでスコープを設定できるように、人々はここからletを使い始めると思います。
JavaScriptでのスコープについて明確に理解していない人は、以前は間違いを犯していました。
let
を使用した巻き上げはサポートされていません。
このアプローチでは、JavaScriptに存在するエラーが取り除かれています。
理解を深めるには、ES6 IN DEPTH:LETおよびCONSTを参照してください。
vs varさせてください。それはすべて scope についてのものです。
var変数はグローバル であり、基本的にどこからでもアクセスできますが、 let変数はグローバルではありません そして閉じ括弧でそれらが消滅するまで存在します。
下記の私の例を見てください、そしてlion(let)変数が2つのconsole.logsでどのように異なるように振る舞うかに注意してください。 2番目のconsole.logでは範囲外になります。
var cat = "cat";
let dog = "dog";
var animals = () => {
var giraffe = "giraffe";
let lion = "lion";
console.log(cat); //will print 'cat'.
console.log(dog); //will print 'dog', because dog was declared outside this function (like var cat).
console.log(giraffe); //will print 'giraffe'.
console.log(lion); //will print 'lion', as lion is within scope.
}
console.log(giraffe); //will print 'giraffe', as giraffe is a global variable (var).
console.log(lion); //will print UNDEFINED, as lion is a 'let' variable and is now out of scope.
上記のように:
違いはスコープです。
var
は最も近い function block の範囲にあり、let
は 最近接囲みblock の範囲にあります。 ブロックの外側にある場合は、どちらもグローバルです。例を見てみましょう。
例1:
私の両方の例で私はmyfunc
という関数を持っています。 myfunc
は10に等しい変数myvar
を含みます。最初の例では、myvar
が10(myvar==10
)に等しいかどうかをチェックします。もしそうなら、私はmyvar
キーワードを使って変数var
(今は2つのmyvar変数があります)を宣言し、それに新しい値を代入します(20)。次の行で、その値をコンソールに表示します。条件付きブロックの後、私は再びmyvar
の値をコンソールに表示します。 myfunc
の出力を見ると、myvar
の値は20です。
例2: 2番目の例では、条件付きブロックでvar
キーワードを使用する代わりに、myvar
をlet
キーワードを使用して宣言します。 myfunc
を呼び出すと、2つの異なる出力myvar=20
とmyvar=10
が得られます。
したがって、違いは非常に単純です。つまり、その範囲です。
実行コンテキストはこれらすべてにおいて重要であるため、これらのキーワードを実行コンテキストにリンクします。実行コンテキストには、作成フェーズと実行フェーズという2つのフェーズがあります。さらに、各実行コンテキストには、可変環境と外部環境(その語彙環境)があります。
実行コンテキストの作成段階では、var、letおよびconstは、与えられた実行コンテキストの変数環境に未定義の値でその変数をメモリに格納します。違いは実行フェーズにあります。値を代入する前にvarで定義された変数を参照すると、未定義になります。例外は発生しません。
ただし、letまたはconstで宣言された変数は、宣言されるまで参照できません。宣言される前にそれを使用しようとすると、実行コンテキストの実行フェーズ中に例外が発生します。実行コンテキストの作成フェーズのおかげで、変数はメモリ内に残ったままになりますが、エンジンでは使用できません。
function a(){
b;
let b;
}
a();
> Uncaught ReferenceError: b is not defined
Varで定義された変数では、エンジンが現在の実行コンテキストの変数環境でその変数を見つけられない場合、スコープチェーン(外部環境)を上ってその変数の外部環境の変数環境を調べます。それが見つからない場合は、スコープチェーンの検索を続けます。これはletとconstの場合には当てはまりません。
Letの2番目の特徴はブロックスコープを紹介することです。ブロックは中括弧で定義されます。例としては、ブロックの場合はブロック、ファンクションブロック、ブロックの場合などがあります。ブロックの内側でletを使用して変数を宣言すると、その変数はブロックの内側でしか使用できません。実際、forループ内などでブロックが実行されるたびに、メモリ内に新しい変数が作成されます。
ES6では、変数を宣言するためのconstキーワードも導入されています。 constもブロックスコープです。 letとconstの違いは、const変数はイニシャライザを使用して宣言する必要があることです。そうしないとエラーが発生します。
そして最後に、実行コンテキストになると、varで定義された変数は 'this'オブジェクトに付加されます。グローバル実行コンテキストでは、それがブラウザのウィンドウオブジェクトになります。これはletやconstには当てはまりません。
私は、用語と例の大部分は少し圧倒されていると思います。私が個人的に違いを持つ主な問題は、「ブロック」が何であるかを理解することです。ある時点で私は気づいた、ブロックはIF
ステートメントを除いて任意の波括弧であろう。関数またはループの左大括弧{
は新しいブロックを定義し、その中にlet
で定義されているものはすべて同じもの(関数またはループ)の右大括弧}
の後には使用できません。それを念頭に置いて、理解するのは簡単でした。
let msg = "Hello World";
function doWork() { // msg will be available since it was defined above this opening bracket!
let friends = 0;
console.log(msg);
// with VAR though:
for (var iCount2 = 0; iCount2 < 5; iCount2++) {} // iCount2 will be available after this closing bracket!
console.log(iCount2);
for (let iCount1 = 0; iCount1 < 5; iCount1++) {} // iCount1 will not be available behind this closing bracket, it will return undefined
console.log(iCount1);
} // friends will no be available after this closing bracket!
doWork();
console.log(friends);
私は現在JavaScriptを深く理解しようとしているので、私はすでに議論した素晴らしい部分のいくつかと別の観点でのいくつかの他の詳細を含む私の簡単な研究を共有するつもりです。
var と let の違いを理解することは、 function と block scope の違いを理解すればより簡単になります。
次のような場合を考えましょう。
(function timer() {
for(var i = 0; i <= 5; i++) {
setTimeout(function notime() { console.log(i); }, i * 1000);
}
})();
Stack VariableEnvironment //one VariablEnvironment for timer();
// when the timer is out - the value will be the same value for each call
5. [setTimeout, i] [i=5]
4. [setTimeout, i]
3. [setTimeout, i]
2. [setTimeout, i]
1. [setTimeout, i]
0. [setTimeout, i]
####################
(function timer() {
for (let i = 0; i <= 5; i++) {
setTimeout(function notime() { console.log(i); }, i * 1000);
}
})();
Stack LexicalEnvironment - each iteration has a new lexical environment
5. [setTimeout, i] [i=5]
LexicalEnvironment
4. [setTimeout, i] [i=4]
LexicalEnvironment
3. [setTimeout, i] [i=3]
LexicalEnvironment
2. [setTimeout, i] [i=2]
LexicalEnvironment
1. [setTimeout, i] [i=1]
LexicalEnvironment
0. [setTimeout, i] [i=0]
timer()
が呼び出されると ExecutionContext が作成され、各反復に対応する VariableEnvironment とすべての LexicalEnvironment の両方が含まれます。
そしてより単純な例
機能範囲
function test() {
for(var z = 0; z < 69; z++) {
//todo
}
//z is visible outside the loop
}
ブロックスコープ
function test() {
for(let z = 0; z < 69; z++) {
//todo
}
//z is not defined :(
}