5648

ECMAScript6はステートメントを導入しましlet

変数として記述されていると聞きましたが、キーワードlocalとの動作の違いはまだよくわかりません。var

違いは何ですか?let?の代わりにいつ使用する必要がありvarますか?

4

39 に答える 39

7497

スコーピング規則

主な違いはスコープ ルールです。キーワードによって宣言された変数はvar、直接の関数本体 (したがって関数スコープ)にスコープされますが、変数は、によって示されるletすぐに囲んで{ }いるブロック(したがってブロック スコープ) にスコープされます。

function run() {
  var foo = "Foo";
  let bar = "Bar";

  console.log(foo, bar); // Foo Bar

  {
    var moo = "Mooo"
    let baz = "Bazz";
    console.log(moo, baz); // Mooo Bazz
  }

  console.log(moo); // Mooo
  console.log(baz); // ReferenceError
}

run();

言語にキーワードが導入された理由letは、関数のスコープがわかりにくく、JavaScript のバグの主な原因の 1 つでした。

別のスタック オーバーフローの質問からこの例を見てください。

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: 3funcs[j]();匿名関数が同じ変数にバインドされているため、呼び出されるたびにコンソールに出力されました。

ループから正しい値を取得するために、すぐに呼び出される関数を作成する必要がありましたが、これも面倒でした。

巻き上げ

varkeyword で宣言された変数は巻き上げられ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"; // not allowed to be globally scoped

console.log(window.foo); // Foo
console.log(window.bar); // undefined

再宣言

厳密モードでは、SyntaxErrorを発生させvarながら、同じスコープで同じ変数を再宣言できます。let

'use strict';
var foo = "foo1";
var foo = "foo2"; // No problem, 'foo1' is replaced with 'foo2'.

let bar = "bar1"; 
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared

于 2012-07-12T02:53:34.897 に答える
171

ここにいくつかの例でキーワードの説明があります。let

letと非常によく似ていvarます。主な違いは、var変数のスコープが囲んでいる関数全体であるということです

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

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

于 2009-04-17T20:11:47.053 に答える
135

受け入れられた答えにはポイントがありません:

{
  let a = 123;
};

console.log(a); // ReferenceError: a is not defined
于 2015-06-02T20:59:17.957 に答える
118

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

ループ内

in ループで宣言された変数は、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;

再申告なし

を使用して同じ変数を複数回宣言することはできませんletletまた、 using で宣言された別の変数と同じ識別子で変数 using を宣言することはできません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 つあります。

再割り当てなし

using で宣言された変数は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
于 2016-11-23T22:52:38.350 に答える
61

微妙な違いがいくつかあります —letスコープの動作は、多かれ少なかれ他の言語での変数のスコープのように動作します。

たとえば、囲んでいるブロックにスコープが設定されている、宣言される前は存在しない、などです。

letただし、これは新しい Javascript 実装の一部にすぎず、さまざまな程度のブラウザー サポートがあることに注意してください。

于 2009-04-17T21:38:22.663 に答える
33
  • 変数が巻き上げられない

    letそれらが出現するブロックの全範囲にホイストすることはありません。対照的に、var以下のようにホイストできます。

    {
       console.log(cc); // undefined. Caused by hoisting
       var cc = 23;
    }
    
    {
       console.log(bb); // ReferenceError: bb is not defined
       let bb = 23;
    }
    

    実際、@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);  
    }
    

    a) イニシャライザ式 b) 各反復 (以前はインクリメント式を評価するため) にこれらの名前を使用して新しいレキシカル環境を作成するためlet、詳細についてはhereを参照してください。

于 2016-01-17T15:11:31.843 に答える
26

これは、他の人がすでに書いたものに追加する例です。関数の配列 を作成するとします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代わりに、ループの最後で、iin 各関数のクロージャーは、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 つの動作の混合をイメージすると、同じスクリプトで新しい動作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。

于 2014-08-18T00:58:29.177 に答える
23

次の 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
}
于 2015-12-17T03:22:03.993 に答える
17

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

于 2016-07-08T00:21:11.467 に答える
17

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

これはコンパイルエラーを生成しません:

var x = 1;
var x = 2;

この意志:

let x = 1;
let x = 2;
于 2015-08-11T00:35:29.740 に答える
17

ES6 では、 varに代わる2 つの新しいキーワード ( letconst ) が導入されました。

ブロック レベルの減速が必要な場合は、var の代わりに let と const を使用できます。

以下の表は、var、let、および const の違いをまとめたものです。

ここに画像の説明を入力

于 2020-01-26T11:39:21.787 に答える
8

いくつかのハック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)
于 2016-07-21T17:42:16.157 に答える
3

私は現在、JavaScript を深く理解しようとしているので、既に説明したいくつかの優れた部分と、別の観点からのその他の詳細を含む私の簡単な調査を共有します。

functionblock scopeの違いを理解すれば、 varletの違いを理解しやすくなります。

次のケースを考えてみましょう。

(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()呼び出されると、 VariableEnvironmentと各反復に対応するすべてのLexicalEnvironmentsの両方を含むExecutionContextが作成されます。

そしてもっと簡単な例

機能範囲

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 :(
}
于 2019-03-11T16:52:45.457 に答える
2

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

const識別子が再割り当てされないというシグナルです。

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

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

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

于 2016-12-27T09:44:55.907 に答える