javascript - react - var vs const




使用“let”和“var”在JavaScript中聲明變量有什麼區別? (18)

ECMAScript 6引入let語句 。 我聽說它被描述為“本地”變量,但我仍然不太確定它與var關鍵字的行為方式有何不同。

有什麼區別? 何時應該使用var


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聲明的變量無法訪問。

{
  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 -it的塊範圍並且具有TDZ。 然而,有兩件事是不同的。

沒有重新分配

使用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

功能VS塊範圍:

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

testLet調用第二個函數時,聲明為的變量bar let只能在if語句中訪問。因為與聲明的變量let塊作用域(其中塊是大括號中的代碼例如if{}for{}function{})。

let 變量不會被掛起:

var和之間的另一個區別let是聲明的變量let 沒有被提升。一個示例是說明此行為的最佳方式:

let 掛起的變量:

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

Global 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

盡可能使用letvar因為它的範圍更加具體。這減少了在處理大量變量時可能發生的潛在命名衝突。var當你想要一個顯式的全局變量在對window像上時,可以使用它(如果真的有必要,請務必仔細考慮)。



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

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

如果您想了解更多,請繼續閱讀以下內容

關於範圍的最著名的訪談問題之一也足以確切使用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,對於每個循環迭代,變量都是作用域並具有共享副本。


以下是let關鍵字解釋和一些示例。

讓我的工作非常像var。 主要區別在於var變量的範圍是整個封閉函數

維基百科上的這個表顯示了哪些瀏覽器支持Javascript 1.7。

請注意,只有Mozilla和Chrome瀏覽器支持它。 IE,Safari和其他人可能沒有。


以下是兩者之間差異的示例(支持剛啟動的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);


可能以下兩個函數顯示了不同之處:

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范圍表達更像是變量範圍在或多或少任何其他語言中。

例如它適用於封閉塊,它們在聲明之前不存在,等等。

但是值得注意的是, let只是新Javascript實現的一部分,並且具有不同程度的瀏覽器支持


本文明確定義了var,let和const之間的區別

const 是一個信號,標識符不會被重新分配。

let,是一個可以重新分配變量的信號,例如循環中的計數器,或算法中的值交換。它還表示變量將僅在其定義的塊中使用,而這並不總是包含整個函數。

var現在是在JavaScript中定義變量時可用的最弱信號。變量可以重新分配,也可以不重新分配,變量可以用於或不用於整個函數,或僅用於塊或循環的目的。

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


正如剛才提到的:

區別在於範圍界定。var的範圍限定為最近的功能塊let並且範圍限定為最近的封閉塊,該封閉塊可以小於功能塊。如果在任何區塊之外,兩者都是全局的。讓我們看一個例子:

例1:

在我的兩個例子中,我都有一個功能myfuncmyfunc包含一個myvar等於10 的變量。在我的第一個例子中,我檢查是否myvar等於10(myvar==10)。如果是,我myvar使用var關鍵字聲明一個變量(現在我有兩個myvar變量)並為其賦值一個新值(20)。在下一行中,我在我的控制台上打印它的值。在條件塊後,我再次打印myvar我的控制台上的值。如果查看輸出myfuncmyvar則值等於20。

示例2 在我的第二個示例中var,我使用關鍵字而不是在條件塊中myvar使用let關鍵字。現在,當我打電話時,myfunc我得到兩個不同的輸出:myvar=20myvar=10

所以區別很簡單,即範圍。


這是一個添加其他人已經寫過的例子。 假設您要創建一個函數數組adderFunctions ,其中每個函數都使用一個Number參數,並返回參數和函數索引在數組中的總和。 嘗試使用var關鍵字生成帶有循環的adderFunctions將無法按照某人可能天真期望的方式工作:

// 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指的是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按預期運行。

現在,圖像混合了兩種行為,你可能會明白為什麼不建議在同一個腳本中將較新的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行為以及函數閉包,這也很容易理解。這將是添加數字的可怕方式。但是在其他環境中的現實世界中可能會遇到在匿名函數閉包中捕獲數據的一般技術。因人而異。


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
}

不同之處在於使用每個變量聲明的變量的scope

在實踐中,範圍差異有許多有用的後果:

  1. let變量僅在最近的封閉塊({ ... })中可見。
  2. let變量只能在聲明變量出現的代碼行中使用(即使它們被掛起!)。
  3. let變量可能不會被後續var或後續重新聲明let
  4. 全局let變量不會添加到全局對window像中。
  5. let變量易於使用閉包(它們不會導致競爭條件)。

通過let降低變量的可見性並增加早期發現意外名稱衝突的可能性而施加的限制。這使得更容易跟踪和推理變量,包括它們的reachability(幫助回收未使用的內存)。

因此,let當在大型程序中使用變量或者以獨立開發的框架以新的和意外的方式組合時,變量不太可能導致問題。

var如果在循環中使用閉包(#5)或在代碼中聲明外部可見的全局變量(#4)時確定需要單綁定效果,那麼仍然有用。var如果export從轉換器空間遷移到核心語言,則可以取代用於導出的用途。

例子

1.不能在最近的封閉塊之外使用:這段代碼將拋出一個引用錯誤,因為第二次使用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.
}

相比之下,使用varparses和run 的相同示例沒有拋出任何異常。

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

...循環意外地輸出“我是5”五次:

i is 5
i is 5
i is 5
i is 5
i is 5

以前在JavaScript中只有兩個範圍,即功能範圍和全局範圍。使用' let'關鍵字JavaScript現在已經引入了block-level變量。

要完全理解'let'關鍵字,ES6:'let'關鍵字在JavaScript中聲明變量會有所幫助。


如果我讀的規範正確,然後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

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
}

我認為人們將在這之後開始使用let,以便他們在JavaScript中具有類似的範圍,如其他語言,Java,C#等。

那些對JavaScript中的作用域不太了解的人過去常犯錯誤。

使用時不支持吊裝let

使用此方法,JavaScript中出現的錯誤將被刪除。

請參閱ES6 In Depth:讓和const更好地理解它。





let