scope const ブロックスコープ - JavaScriptで変数を宣言するために "let"と "var"を使用する違いは何ですか?





13 Answers

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

DEMO

for(var i = 1; i < 6; i++) {
  document.getElementById('my-element' + i)
    .addEventListener('click', function() { alert(i) })
}

上のコードは、古典的なJavaScriptクロージャの問題を示しています。 i変数の参照は、 iの実際の値ではなく、クリックハンドラクロージャに格納されています。

すべてのクリックハンドラは同じオブジェクトを参照します。これは、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) })
}
定数 変数の巻き上げ 対応ブラウザ

ECMAScript 6 letステートメントを導入しまし 。 私はそれが "ローカル"変数として記述されていると聞いたことがありますが、私はそれがvarキーワードとは異なる動作をするかどうかはまだはっきりしていません。

違いは何ですか? いつvar上で使用letれるべきですか?




letvar違いは何ですか?

  • 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コードを記述していて、Transizer( Traceur )を使用している場合は、 letステートメントを安全に使用することができますが、コードはパフォーマンスに関して最適なものになる可能性があります。

  • クライアントサイドのJavaScriptコードを記述していて、トランスパイライザを使用していない場合は、ブラウザのサポートを考慮する必要があります。

今日、2018年6月8日、まだサポートしていないブラウザがいくつかあります。

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

この回答を読んでいるときにletステートメントをサポートしているブラウザの最新の概要については、 このCan I Use 」ページを参照してください

(*)グローバル変数と関数スコープ変数は、JavaScript変数がhoistedれて宣言される前に初期化され、使用されます。 これは、宣言が常にスコープの上端にあることを意味します。

(**)ブロックスコープ変数は持ち上げられません




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

関数の内部

関数の中で(ブロックの外に)、 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を使って宣言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ループで宣言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を使うと、各繰り返しで新しい変数が得られます。 つまり、ループ内でクロージャを安全に使用することができます。

// 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を使って宣言された変数は宣言letれる前にアクセスできません。 そうしようとするとエラーがスローされます。

console.log(noTDZ); // undefined
var noTDZ = 43;
console.log(hasTDZ); // ReferenceError: hasTDZ is not defined
let hasTDZ = 42;

再宣言しない

letを使って同じ変数を複数回宣言することはできません。 varを使って宣言された別の変数と同じ識別子を持つletを使って変数を宣言することもできません。

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 -itのブロックスコープと非常によく似ており、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つの違いの例を示します(クロームの開始をサポートしています)。

ご覧のように、変数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はforループのように宣言されたスコープ内でしか使用できませんが、 varはループの外側でアクセスすることができます。 MDNの文書( MDNの例も)から:

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

letによって宣言された変数は、それらが定義されているブロックだけでなく、含まれているサブブロックのスコープを持ちます。 このように、 varは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
}`

プログラムや関数の最上位レベルでは、 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に転送する方がよいでしょう。訪問バベルのウェブサイト




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

実際には、スコープの違いによる多くの有益な結果があります。

  1. 変数は最も近い囲みブロック( { ... } )でのみ表示されます。
  2. let変数は、変数が宣言されたに発生するコード行でのみ使用できます(エレベーターに乗っていても!)。
  3. let変数は後続のvarまたはによって再宣言されることはできませんlet
  4. グローバルlet変数はグローバルwindowオブジェクトに追加されません。
  5. let変数はクロージャで簡単に使用できます(競合条件は発生しません)。

この制限によってlet、変数の可視性が低下し、予期しない名前の衝突が早期に発見される可能性が高まります。これにより、reachability(未使用メモリの再利用に役立つ)を含む、変数の追跡と理由付けが容易になります。

その結果、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



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および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 感謝の意をもって私的なメンバーをシミュレートする自己呼び出し関数を避けるためにも活用することができます。コードの可読性を低下させ、デバッグを複雑にし、コードを保護しません。セマンティクスの欲求なので、使用を中止してください。/暴言

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

letキーワードは、どのブロック(通常の範囲に変数宣言添付{ .. }それに記載されたペア)。換言すれば、let暗黙的にその変数宣言するための任意のブロックの範囲をハイジャック。

letwindowオブジェクトにグローバルにアクセスすることはできないため、変数にアクセスすることはできません。

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の変数には関数内でスコープがあり、変数が関数内で有効であり、関数自体の外部ではないことを意味します。

varwindowオブジェクトにグローバルにアクセスすることができないため、変数にアクセスすることができます。

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ループの繰り返しごとに変数のスコープが設定され、共有コピーがあるためです。




以前は、JavaScriptではスコープが2つしかなく、機能的でグローバルなものでした。' let'キーワードを使用すると、JavaScriptがblock-level変数を導入しました。

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




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

const 識別子が再割当てされないことを示す信号である。

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

varJavaScriptで変数を定義すると、最も弱い信号になりました。変数は再割り当てされてもされなくてもよく、変数は関数全体またはブロックまたはループの目的のためだけに使用されても使用されなくてもよい。

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







Related