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
內部功能
在函數內部(但在塊之外), 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
-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
?
盡可能使用let
,var
因為它的範圍更加具體。這減少了在處理大量變量時可能發生的潛在命名衝突。var
當你想要一個顯式的全局變量在對window
像上時,可以使用它(如果真的有必要,請務必仔細考慮)。
ECMAScript 6增加了一個關鍵字來聲明除“let”以外的“const”變量。
在“var”上引入“let”和“const”的主要目的是使用塊方法而不是傳統的詞法範圍。本文非常簡要地解釋了“var”和“let”之間的區別,它還涵蓋了對“const”的討論。
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
如果您想了解更多,請繼續閱讀以下內容
關於範圍的最著名的訪談問題之一也足以確切使用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
,對於每個循環迭代,變量都是作用域並具有共享副本。
以下是兩者之間差異的示例(支持剛啟動的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:
在我的兩個例子中,我都有一個功能myfunc
。myfunc
包含一個myvar
等於10 的變量。在我的第一個例子中,我檢查是否myvar
等於10(myvar==10
)。如果是,我myvar
使用var
關鍵字聲明一個變量(現在我有兩個myvar變量)並為其賦值一個新值(20)。在下一行中,我在我的控制台上打印它的值。在條件塊後,我再次打印myvar
我的控制台上的值。如果查看輸出myfunc
,myvar
則值等於20。
示例2 :在我的第二個示例中var
,我使用關鍵字而不是在條件塊中myvar
使用let
關鍵字。現在,當我打電話時,myfunc
我得到兩個不同的輸出:myvar=20
和myvar=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
這一次, i
在for
循環的每次迭代中都會反彈。 現在,每個函數在函數創建時都保留i
的值, adderFunctions
按預期運行。
現在,圖像混合了兩種行為,你可能會明白為什麼不建議在同一個腳本中將較新的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
行為以及函數閉包,這也很容易理解。這將是添加數字的可怕方式。但是在其他環境中的現實世界中可能會遇到在匿名函數閉包中捕獲數據的一般技術。因人而異。
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。
在實踐中,範圍差異有許多有用的後果:
-
let
變量僅在最近的封閉塊({ ... }
)中可見。 -
let
變量只能在聲明變量後出現的代碼行中使用(即使它們被掛起!)。 -
let
變量可能不會被後續var
或後續重新聲明let
。 - 全局
let
變量不會添加到全局對window
像中。 -
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.
}
相比之下,使用var
parses和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更好地理解它。