ECMAScript 6 はlet
ステートメントを導入しました。
「ローカル」変数として記述されていると聞きましたが、var
キーワードとどのように異なる動作をするのかはまだよくわかりません。
違いは何ですか?いつlet
使用すべきvar
ですか?
ECMAScript 6 はlet
ステートメントを導入しました。
「ローカル」変数として記述されていると聞きましたが、var
キーワードとどのように異なる動作をするのかはまだよくわかりません。
違いは何ですか?いつlet
使用すべきvar
ですか?
回答:
主な違いはスコーピングルールです。var
キーワードによって宣言された変数は、直接の関数本体(つまり、関数スコープ)にlet
スコープが設定され、変数は、(したがって、ブロックスコープ)で示されるすぐ外側のブロックにスコープが設定され{ }
ます。
function run() {
var foo = "Foo";
let bar = "Bar";
console.log(foo, bar);
{
let baz = "Bazz";
console.log(baz);
}
console.log(baz); // ReferenceError
}
run();
let
キーワードが言語に導入された理由は、関数のスコープであり、JavaScriptのバグの主な原因の1つでした。
別のstackoverflowの質問からこの例を見てみましょう:
var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
// and store them in funcs
funcs[i] = function() {
// each should log its value.
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
// and now let's run each one to see
funcs[j]();
}
My value: 3
funcs[j]();
匿名関数が同じ変数にバインドされているため、呼び出されるたびにコンソールに出力されました。
ループから正しい値を取得するために、すぐに呼び出される関数を作成する必要がありましたが、これもやっかいでした。
var
キーワードで宣言された変数はホイストされます(undefined
コードが実行される前に初期化されます)。つまり、宣言される前であっても、それらの囲みスコープでアクセスできます。
function run() {
console.log(foo); // undefined
var foo = "Foo";
console.log(foo); // Foo
}
run();
let
変数は、その定義が評価されるまで初期化されません。初期化の前にそれらにアクセスすると、になりますReferenceError
。ブロックの開始から初期化が処理されるまでの間、「一時的なデッドゾーン」にあると言われる変数。
function checkHoisting() {
console.log(foo); // ReferenceError
let foo = "Foo";
console.log(foo); // Foo
}
checkHoisting();
トップレベルではlet
、とは異なりvar
、グローバルオブジェクトにプロパティは作成されません。
var foo = "Foo"; // globally scoped
let bar = "Bar"; // globally scoped
console.log(window.foo); // Foo
console.log(window.bar); // undefined
厳密モードでvar
はlet
、SyntaxErrorが発生する間、同じスコープで同じ変数を再宣言できます。
'use strict';
var foo = "foo1";
var foo = "foo2"; // No problem, 'foo' is replaced.
let bar = "bar1";
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared
let
ブロック式let (variable declaration) statement
は非標準であり、将来は削除されます、bugzilla.mozilla.org / show_bug.cgi?id = 1023609。
let
クロージャの問題を回避するためにも使用できます。以下の例に示すように、古い参照を保持するのではなく、新しい値をバインドします。
for(var i=1; i<6; i++) {
$("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Clicking on each number will log to console:</p>
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>
上記のコードは、古典的なJavaScriptクロージャの問題を示しています。i
変数への参照は、の実際の値ではなく、クリックハンドラクロージャに格納されていますi
。
6を保持するカウンターオブジェクトが1つしかないため、各クリックハンドラーは同じオブジェクトを参照し、クリックごとに6つを取得します。
一般的な回避策は、これを無名関数でラップi
し、引数として渡すことです。このような問題は、以下のコードに示すようにlet
代わりにvar
を使用することで回避することもできます。
(ChromeおよびFirefox 50でテスト済み)
for(let i=1; i<6; i++) {
$("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Clicking on each number will log to console:</p>
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>
let
、すべてのボタンに対して「6」を警告します。どのようlet
に動作することになっていると言うソースはありますか?
let
本当に役立つのかがわかります。ループ内でイベントリスナーを設定する場合i
、各反復でローカルにスコープを設定するために、すぐに呼び出される関数式は必要ありません。
let
とはvar
?var
ステートメントを使用して定義された変数は、関数の最初から、それが定義されている関数全体で認識されます。(*)let
ステートメントを使用して定義された変数は、それが定義された瞬間から、それが定義されているブロックでのみ認識されます。(**)違いを理解するには、次のコードを検討してください。
// 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の第一人者であるカイル・シンプソンは、彼がそうではないと信じる理由について非常に精巧な記事を書きました。
しかし、今日では、そうではありません。実際、このlet
ステートメントを使用しても安全かどうかを自問する必要があります。その質問に対する答えは、環境によって異なります。
サーバーサイドJavaScriptコード(Node.js)をlet
記述している場合は、ステートメントを安全に使用できます。
クライアント側のJavaScriptコードを記述していて、ブラウザーベースのトランスパイラー(Traceurやbabel-standaloneなど)を使用している場合は、let
ステートメントを安全に使用できますが、コードはパフォーマンスに関して最適ではない可能性があります。
クライアント側のJavaScriptコードを記述していて、ノードベースのトランスパイラー(traceurシェルスクリプトやBabelなど)を使用している場合は、let
ステートメントを安全に使用できます。また、ブラウザーはトランスパイルされたコードのみを認識するため、パフォーマンスの低下は制限されます。
クライアント側のJavaScriptコードを記述していて、トランスパイラーを使用しない場合は、ブラウザーのサポートを検討する必要があります。
まだまったくサポートlet
していないブラウザもあります:
let
この回答を読んだ時点でのステートメントをサポートしているブラウザーの最新の概要については、このCan I Use
ページを参照してください。
(*)JavaScript変数がホイストされるため、グローバルおよび機能的にスコープされた変数は、宣言する前に初期化して使用できます。これは、宣言が常にスコープの一番上にあることを意味します。
(**)ブロックスコープの変数は巻き上げられません
i
機能ブロックのどこにでもあります!undefined
値を割り当てるまで(巻き上げによる)として始まります!ps:let
も(その包含ブロックの最上部まで)巻き上げられReferenceError
ますが、最初の割り当ての前にブロックで参照されると、を提供します。(ps2:私は親セミコロンですが、ブロックの後にセミコロンは本当に必要ありません)。そうは言っても、サポートに関するリアリティチェックを追加してくれてありがとう!
let
及びをvar
!
let
とconst
し、あなたが実際に彼らの追加機能が必要な場合にのみ使用することをお勧め強制/「より多くの仕事で結果(書き込み専用constのような)これらの追加機能をチェックするので、 '(およびスコープツリー内の追加のスコープノード)((current)engine(s)for enforce / check / verify / setup。
ここではlet
、いくつかの例を使用してキーワードの説明を示します。
let
のように機能しvar
ます。主な違いは、var
変数のスコープがそれを囲む関数全体であることです。
ウィキペディアのこの表は、Javascript 1.7をサポートするブラウザーを示しています。
MozillaおよびChromeブラウザのみがサポートしていることに注意してください。IE、Safari、そして潜在的に他の人はそうしません。
受け入れられた答えはポイントを欠いています:
{
let a = 123;
};
console.log(a); // ReferenceError: a is not defined
for
ループ初期化子でそれを示しただけであり、の制限の適用範囲を劇的に狭めましたlet
。賛成。
let
let
キーワードを使用して宣言された変数はブロックスコープです。つまり、変数は宣言されたブロックでのみ使用できます。
トップレベルでは、を使用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
inループで宣言された変数は、そのループ内でのみ参照できます。
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
2つの違いの例を次に示します(Chromeでサポートが開始されたばかりです)。
ご覧のとおり、var j
変数はまだforループスコープ(ブロックスコープ)外の値を持っていますが、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実装の一部にすぎず、さまざまな程度のブラウザサポートがあることは注目に値します。
let
、第6版のドラフトに含まれており、最終的な仕様に含まれる可能性が高いことにも注意する必要があります。
let
です。Safari、IE、Chomeはすべてサポートしていません。
let
によって定義された変数を使用するのに適していますlet
。if
ほんの数行のコード以上のステートメントがある場合、定義されるまでその変数を使用できないことを忘れるかもしれません。素晴らしいポイント!!!
let
ホイストませんブロックの先頭に変数をしかし、中に変数宣言結果の前にブロック内の変数を参照。にReferenceError(私注:代わりに、古き良きのundefined
)。変数は、ブロックの開始から宣言が処理されるまでの「一時的なデッドゾーン」にあります。」「基になるブロックが1つしかないため、switchステートメント」も同様です。出典:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
主な違いはスコープの違いですが、letは宣言されたスコープ内でのみ使用できます。たとえば、forループの場合と同様に、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 Webサイトなどを使用してECMA5にトランスパイルすることをお勧めします。
巻き上げない変数
let
うではないホイスト彼らが現れるブロックの範囲全体に。対照的に、var
以下のように吊り上げることができます。
{
console.log(cc); // undefined. Caused by hoisting
var cc = 23;
}
{
console.log(bb); // ReferenceError: bb is not defined
let bb = 23;
}
実は、@ 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)各反復(以前はインクリメント式を評価するため)の名前で新しい字句環境を作成するため、詳細はこちらです。
これは、他の人がすでに書いたものに追加する例です。関数の配列を作成するとしますadderFunctions
。ここで、各関数は1つの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
in each関数のクロージャーは、i
のすべての匿名関数のループの終わり(1000)でのの値を参照しadderFunctions
ます。これは、私たちが望んでいたことではありません。まったく同じ動作をするメモリ内に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。
let value = i;
。for
声明は、字句ブロックを作成します。
違いは、それぞれで宣言された変数のスコープにあります。
実際には、スコープの違いには多くの有用な結果があります。
let
変数は、最も近い囲みブロック({ ... }
)でのみ表示されます。let
変数は、変数が宣言された後に発生するコード行でのみ使用できます(たとえホイストされていても!)。let
変数は、後続のvar
orで再宣言できませんlet
。let
変数はグローバルwindow
オブジェクトに追加されません。let
変数はクロージャーで簡単に使用できます(競合状態を引き起こしません)。let
変数の可視性が低下し、予期しない名前の衝突が早期に発見される可能性が高まることによって課される制限。これにより、到達可能性を含む変数の追跡と推論が容易になります(未使用のメモリの回収に役立ちます)。
その結果、let
大きなプログラムで使用したり、独自に開発したフレームワークを新しい予期しない方法で組み合わせたりすると、変数が問題を引き起こす可能性が低くなります。
var
ループでクロージャーを使用する場合(#5)またはコード内で外部から見えるグローバル変数を宣言する場合(#4)にシングルバインディング効果が必要な場合は、それでも便利です。トランスパイラースペースからコア言語に移行するvar
場合、エクスポートにを使用することはできませんexport
。
1.最も近い囲みブロックの外では使用しない:
このコードブロックは、2番目の使用が次のようにx
宣言されているブロックの外で発生するため、参照エラーをスローします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 i = 0; while (i < 5) { doSomethingLater(); i++; }
i
。isはクロージャーの外側にあり、doSomethingLater()
実行されるまでに、i
すでに5回インクリメントされているため、出力はi is 5
5回です。を使用することによりlet
、変数i
はクロージャ内にあるため、でi
作成された「グローバル」な呼び出しを使用する代わりに、各非同期呼び出しは独自のコピーを取得しvar
ます。
for
。より正確な変換は、より複雑ですが、古典的なfor (var i = 0; i < 5; i++) { (function(j) { setTimeout(_ => console.log(
iは$ {j}です。), 125/*ms*/); })(i); }
これは、「関数アクティブ化レコード」を導入して、の各値を関数内のi
名前で保存j
します。
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]のみをカウントします。
との主な違いはvar
、let
で宣言された変数var
が関数スコープであることです。で宣言された関数let
はブロックスコープです。例えば:
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
が呼び出されても、で宣言された変数foo var
は、if
ステートメントの外部から引き続きアクセスできます。この変数foo
は、関数のスコープ内のどこでも使用できtestVar
ます。
変数let
:
2番目の関数testLet
が呼び出されると、で宣言された変数bar let
はif
ステートメント内でのみアクセスできます。変数を宣言しているのでlet
されているブロックスコープのブロックは、例えば波括弧の間にコードです(if{}
、for{}
、function{}
)。
let
変数は上昇しません:とのもう1つの違いはvar
、let
で宣言された変数let
が巻き上げられないことです。例は、この動作を説明する最良の方法です。
巻き上げられlet
ない変数:
console.log(letVar);
let letVar = 10;
// referenceError, the variable doesn't get hoisted
変数var
は巻き上げられます:
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
ます(これが本当に必要な場合は常に注意深く検討してください)。
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
var
ES5の変数のスコープは関数内にあり、変数は関数内で有効であり、関数自体の外では有効ではありません。
var
変数はwindow
グローバルにアクセスできないため、オブジェクト内でアクセスできます。
function a(){ // this is the Max Scope for var variable
{
var x = 12;
}
console.log(x);
}
a(); // 12
もっと知りたい場合は、以下をお読みください
スコープ上で最も有名なインタビューの質問の一つは、またの正確な使用を十分することができます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
、ループの反復ごとに変数のスコープが設定され、コピーが共有されるためです。
for (let i = 0; i < 5; i++) {
// i accessible ✔️
}
// i not accessible ❌
for (var i = 0; i < 5; i++) {
// i accessible ✔️
}
// i accessible ✔️
⚡️サンドボックスで遊ぶ↓
私は右のそれから仕様を読めばlet
ありがたいことも避けるのに活用することができる機能を呼び出す自己 -プライベートメンバーのみをシミュレートするために使用多分誰かさんを満たす除いて-本当のコード保護または他の利点を追加していないこと、コードの可読性を低下させる人気のデザインパターン、デバッグ複雑化をセマンティクスを望んでいるので、使用を中止してください。/ 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
「を参照してくださいプライベート・インタフェースをエミュレート」
let
か?(私はあなたが「自己呼び出し機能」を備えたIIFEを意味すると思います。)
hiddenProperty
コンストラクタで設定するのですか?hiddenProperty
「クラス」内のすべてのインスタンスに1つだけあります。
いくつかのハック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 { type, name, value } = node;
ですか?タイプ/名前/値の3つのプロパティを持つ新しいオブジェクトを作成し、ノードのプロパティ値でそれらを初期化しますか?
var
ます。
対変数。すべてはスコープについてです。
var変数はグローバルであり、基本的にどこからでもアクセスできますが、let変数はグローバルではなく、閉じ括弧でそれらを削除するまで存在します。
以下の私の例を参照し、ライオン(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.
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
}
以下は、「let」と「var」がスコープでどのように異なるかを示しています。
let gfoo = 123;
if (true) {
let gfoo = 456;
}
console.log(gfoo); // 123
var hfoo = 123;
if (true) {
var hfoo = 456;
}
console.log(hfoo); // 456
gfoo
定義され、let
当初にあるグローバルスコープ、と私たちは宣言したときにgfoo
内部で再びif clause
その範囲に変更し、新しい値がそのスコープ内の変数に代入されたとき、それは影響を与えませんグローバルスコープを。
一方hfoo
で定義され、var
中に最初にあるグローバルスコープが、私たちは内部でそれを宣言したときに再びif clause
varはそれを宣言するために再び使用されているが、それは、グローバルスコープのhfooを検討します。そして、その値を再割り当てすると、グローバルスコープhfooも影響を受けることがわかります。これが主な違いです。
上記のように:
違いはスコープです。
var
は最も近い関数ブロックをlet
スコープとし、最も近い囲みブロックをスコープとします。これは、関数ブロックよりも小さくすることができます。いずれかのブロックの外側にある場合、どちらもグローバルです。例を見てみましょう。
例1:
私の両方の例では、関数がありますmyfunc
。10に等しいmyfunc
変数myvar
が含まれています。最初の例でmyvar
は、10(myvar==10
)に等しいかどうかを確認します。はいの場合、キーワードをmyvar
使用して変数を宣言し (現在は2つのmyvar変数があります)var
、新しい値(20)を割り当てます。次の行で、その値をコンソールに出力します。条件付きブロックの後、もう一度myvar
コンソールにの値を出力します。あなたはの出力を見ればmyfunc
、 myvar
値は20に等しいです。
例
2 :var
条件ブロックでキーワードを使用する代わりに2番目の例で、keyword を使用して宣言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番目の機能は、ブロックスコープを導入することです。ブロックは中括弧で定義されます。例には、関数ブロック、ifブロック、forブロックなどが含まれます。ブロック内で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);
今、私は使用してステートメントのブロックに変数のより良いスコープがあると思います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の詳細:letとconstを参照してください。
この記事では、var、let、constの違いを明確に定義しています
const
識別子が再割り当てされないことを示す信号です。
let
は、ループ内のカウンターやアルゴリズム内の値スワップなど、変数を再割り当てできることを示す信号です。また、変数が定義されているブロックでのみ使用されることを通知します。これは、必ずしもそれを含む関数全体ではありません。
var
JavaScriptで変数を定義するときに利用できる最も弱いシグナルになりました。変数は再割り当てされる場合とされない場合があり、変数は関数全体で使用される場合と使用されない場合があります。
https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b
let
、第6版のドラフトに含まれており、おそらく最終的な仕様に含まれます。