web-dev-qa-db-ja.com

"let"と "var"を使用することの違いは何ですか?

ECMAScript 6では let文が導入されました

「ローカル」変数として記述されていると聞いたことがありますが、それがvarキーワードとは異なる動作をするかどうかはまだよくわかりません。

違いは何ですか? letvarの上でいつ使用すべきですか?

3830
TM.

違いはスコープです。 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
}

ブロック:

これが違いです。 letfor()ループ内でのみ表示され、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.
5094
ThinkingStiff

letはクロージャーに関する問題を避けるためにも使うことができます。以下の例に示すように、古い参照を保持するのではなく、新しい値をバインドします。

_ demo _

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) })
}
505
Gurpreet Singh

letvarの違いは何ですか?

  • 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をまったくサポートしないブラウザーがまだあります:

Support table


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

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


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

** ブロックスコープ変数はホイストされません

150
John Slegers

これは letキーワードの説明です いくつかの例を示します。

varと非常によく似た動作をさせてください。主な違いは、var変数の有効範囲はそれを囲む関数全体であるということです。

この表 ウィキペディアの/は、どのブラウザがJavascript 1.7をサポートしているかを示しています。

MozillaとChromeブラウザだけがそれをサポートしていることに注意してください。 IE、Safari、そして潜在的に他の人はそうではありません。

138
Ben S

受け入れられた答えはポイントを逃しています:

{
  let a = 123;
};

console.log(a); // ReferenceError: a is not defined
104
Lcf.vs

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

関数の中

関数の内部(ただしブロックの外部)では、letvarと同じ有効範囲を持ちます。

(() => {
  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

constletと非常によく似ています - ブロックスコープで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
59

これは両者の違いの例です(サポートはchromeから始まったばかりです):enter image description here

ご覧のとおり、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);

42
vlio20

微妙な違いがいくつかあります - letスコープは、他の言語の変数スコープとほとんど同じように動作します。 

例えばそれは囲んでいるブロックまで及んでいます、それらは宣言される前に存在しません、など。

しかしletは新しいJavascriptの実装の一部にすぎず、さまざまな程度の ブラウザサポート を持っていることに注目する価値があります。

42
olliej

主な違いはscopeの違いですが、letのみ利用可能ですforループのように、scopeスコープ内で、varたとえば、ループの外側にアクセスします。 MDN のドキュメントから(例もMDNから):

letを使用すると、スコープが使用されるブロック、ステートメント、または式に限定された変数を宣言できます。これは、varキーワードとは異なり、ブロックスコープに関係なく、関数全体に対して変数をグローバルに、またはローカルに定義します。

letで宣言された変数のスコープは、変数が定義されているブロックと、含まれているサブブロックにあります。このように、letvarと非常によく似ています。主な違いは、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
}`

プログラムと関数の最上位レベルでは、letvarとは異なり、グローバルオブジェクトにプロパティを作成しません。例えば:

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

22
Alireza
  • 吊りない変数

    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、 varletの両方がホイストされています

  • ガベージコレクション

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

    ただし、varletに置き換えます。

    // print 1, 2, 3, 4, 5. now
    for (let i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }
    

    letは、a)イニシャライザ式b)各反復(以前の増分式の評価)の名前で新しい字句環境を作成するため、詳細は here です。

20
zangw

これは他の人がすでに書いたことに追加する例です。関数の配列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

今回は、iforループの各反復でリバウンドされます。各関数は関数の作成時にiの値を保持するようになり、adderFunctionsは期待通りに動作します。

さて、2つの振る舞いをイメージミキシングすると、おそらく同じスクリプト内で新しいletconstを古い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.

14
abroz

違いは、それぞれで宣言された変数の scope にあります。

実際には、範囲の違いにより多くの有用な結果があります。

  1. let変数は、最近接囲みブロック({ ... })でのみ表示されます。
  2. let変数は、発生するコード行でのみ使用できますafter変数は宣言されます( それらが持ち上げられます !)。
  3. let変数は、後続のvarまたはletで再宣言することはできません。
  4. グローバルlet変数は、グローバルwindowオブジェクトに追加されません。
  5. 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
14
mormegil

機能VSブロックスコープ:

varletの主な違いは、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を持つ変数:

最初の関数testVarvarで宣言された変数fooを呼び出すと、ifステートメントの外部からアクセスできます。この変数fooは、testVarのスコープ内でeverywhereが使用可能になりますfunction

letを持つ変数:

2番目の関数testLetletで宣言された変数barを呼び出すと、ifステートメント内でのみアクセスできます。 letで宣言された変数はブロックスコープであるため(ブロックは中括弧で囲まれたコードです(例:if{}for{}function{})。

let変数は引き上げられません:

varletのもう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

グローバルletwindowにアタッチされません:

グローバルスコープ(関数内にないコード)で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


letvarを使用する必要がある場合

スコープがより限定されているため、できる限り、letではなくvarを使用してください。これにより、多数の変数を処理するときに発生する可能性のある名前の競合が減少します。 varは、グローバル変数をwindowオブジェクトに明示的に含める場合に使用できます(これが本当に必要な場合は、常に慎重に検討してください)。

11

次の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
}
10
Abdennour TOUMI

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]だけをカウントします。

10
Dmitry

仕様を正しく読んだ場合、letthankfullyも活用して、 自己呼び出し関数 をシミュレートするプライベートのみのメンバー-コードの可読性を低下させ、デバッグを複雑にし、実際のコード保護やその他の利点を追加しない人気のあるデザインパターン-セマンティクスに対する誰かの欲求を満たすことができる場合を除き、使用をやめる/ 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

プライベートインターフェイスのエミュレート 」を参照してください

6

また、少なくともVisual Studio 2015のTypeScript 1.5では、 "var"はブロック内で同じ変数名の複数の宣言を許可し、 "let"は許可しません。

これによってコンパイルエラーが発生することはありません。

var x = 1;
var x = 2;

この意志:

let x = 1;
let x = 2;
6
RDoc

varはグローバルスコープ(ホイスト可能)変数です。

letconstはブロックスコープです。

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

5

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つはまた下記のようにletvarの正確な使用で十分です。

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を使用すると、ループの反復ごとに変数がスコープ指定され、コピーが共有されるためです。

4
Ankur Soni

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)
3
zloctb

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
}
2
vipul jain

これまでJavaScriptには機能的なものとグローバルなものの2つのスコープしかありませんでした。 'let'キーワードにより、JavaScriptはblock-level変数を導入しました。

'let'キーワードを完全に理解するには、ES6:JavaScriptで変数を宣言するための 'let'キーワードが役に立ちます。

1
Hitesh Garg

この記事では、var、let、constの違いを明確に定義しています

constは、識別子が再割り当てされないことを示すシグナルです。

letは、ループ内のカウンタ、またはアルゴリズム内の値スワップなど、変数を再割り当てできることを示すシグナルです。また、で定義されているブロック内でのみ変数が使用されることを知らせるというシグナルも含まれます。これは必ずしも包含関数全体ではありません。

JavaScriptで変数を定義した場合、varが最も弱いシグナルになりました。変数は再割り当てされてもされなくてもよく、変数は関数全体に使用されてもされなくてもよく、または単にブロックまたはループの目的に使用されてもされなくてもよい。

https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b

1
anandharshan

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を参照してください。

1
swaraj patil

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.
1
daCoda

上記のように:

違いはスコープです。 varは最も近い function block の範囲にあり、let 最近接囲みblock の範囲にあります。 ブロックの外側にある場合は、どちらもグローバルです。例を見てみましょう。

例1:

私の両方の例で私はmyfuncという関数を持っています。 myfuncは10に等しい変数myvarを含みます。最初の例では、myvarが10(myvar==10)に等しいかどうかをチェックします。もしそうなら、私はmyvarキーワードを使って変数var(今は2つのmyvar変数があります)を宣言し、それに新しい値を代入します(20)。次の行で、その値をコンソールに表示します。条件付きブロックの後、私は再びmyvarの値をコンソールに表示します。 myfuncの出力を見ると、myvarの値は20です。 

let keyword

例2: 2番目の例では、条件付きブロックでvarキーワードを使用する代わりに、myvarletキーワードを使用して宣言します。 myfuncを呼び出すと、2つの異なる出力myvar=20myvar=10が得られます。

したがって、違いは非常に単純です。つまり、その範囲です。 

1
N Randhawa

enter image description here

この画像を見て、私はconstlet変数のデモンストレーションのためのとても簡単な例を一つ作成しました。ご覧のとおり、const変数を変更しようとすると、エラーが発生します( 定数 'name'をオーバーライドしようとしています )。ただし、let variableを見てください。 

最初にlet age = 33を宣言し、後で別の値age = 34;を代入します。これは問題ありません。変数letを変更しようとしてもエラーは発生しません。

0
Mile Mijatovic

実行コンテキストはこれらすべてにおいて重要であるため、これらのキーワードを実行コンテキストにリンクします。実行コンテキストには、作成フェーズと実行フェーズという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には当てはまりません。

0
Donato

私は、用語と例の大部分は少し圧倒されていると思います。私が個人的に違いを持つ主な問題は、「ブロック」が何であるかを理解することです。ある時点で私は気づいた、ブロックは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);
0
Dementic

私は現在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 :(
}
0
Lucian Nut