一、為什么要是用let和const代替var聲明變量
1、var聲明的變量是函數作用域的,存在變量的提升。(變量的聲明會被提前到當前作用域的最前面)
1 //變量提升
2 function f(condition){ 3 if(condition){ 4 var value="blue"; 5 return value; 6 } 7 else{ 8 console.log(value); //undefined
9 } 10 console.log(value); //undefined
11 } 12 f(false);
由於var聲明的變量存在變量提升,所以當condition為false時,輸出變量不會提示value變量未定義的錯誤,上面的代碼類似於
1 //變量提升
2 function f(condition){ 3 var value; 4 if(condition){ 5 value="blue"; 6 return value; 7 } 8 else{ 9 console.log(value); //undefined
10 } 11 console.log(value); //undefined
12 } 13 f(false);
2、var可以在同一作用域內聲明一個變量多次。變量的值為最后一次聲明的
1 var value=10; 2 var value="100"; 3 console.log(value)//100
3、全局作用域的綁定
使用var聲明的全局變量會自動綁定為全局對象的屬性。從而又可能覆蓋已經存在的全局屬性。
1 console.log(global.RegExp);//[Function: RegExp]
2 var RegExp="aaa"; 3 console.log(global.RegExp);//"aaa"
代碼不要寫在js文件中使用node 文件名去測試,因為node會把一個文件作為一個模塊,聲明的變量不是全局對象的屬性。
可以node的repl中測試或者瀏覽器環境中。
使用let和const聲明的變量不會覆蓋全局對象的屬性,而是遮蓋他
1 let RegExp="aaa"; 2 console.log(global.RegExp===RegExp);//false
二、let和const
通過let和const聲明的變量是塊級作用域,只在{}生效;類C語言多采用塊級作用域,這樣做更符合我們的習慣。
let聲明
a、let聲明的變量是塊級作用域,且不存在變量提升機制,所以使用let聲明的 變量只有放到當前作用域的最前面,才能在整個作用域中使用。
1 function f(condition){ 2 if(condition){ 3 let value="blue"; 4 return value; 5 } 6 else{ 7 //console.log(value); //ReferenceError: value is not defined
8 } 9 console.log(value); //ReferenceError: value is not defined
10 } 11 f(true);
變量離開if塊就會被銷毀,其他的塊作用由於沒有什么變量vaue,所以直接使用會拋出異常。
b、let聲明的變量在同一作用域中禁止重復聲明
1 //var value=10;
2 let value=10; 3 let value="100";//SyntaxError: Identifier 'value' has already been declared
4 console.log(value)//100
當在不同的作用域聲明變量的時候,不會報錯。
1 //var value=10;
2 let value=10; 3 function f(){ 4 let value="1dad"; 5 console.log(value)//'1dad'
6 } 7 console.log(value)//10
8 f()
const聲明
a.const關鍵字用來聲明一個常量,聲明后變量的值不可以更改。const聲明變量必須初始化。
1 const a='dada'
2 const b;//SyntaxError: Missing initializer in const declaration
b、const聲明已存在的變量會報錯,(無論是let和const)
var a=10; //let a=10
const a='dada'//Identifier 'a' has already been declared
c、用const聲明對象
const聲明的變量不允許修改變量的值。由於變量存在的是對象的值得引用,所以const聲明的對象可以修改對象的屬性,但是不能把修改對象的引用。
const a={name:'yiluhuakai'}; a.name="aaa"; console.log(a.name)//'aaa'
a={};//TypeError: Assignment to constant variable.
三、臨時死區
使用let和const聲明的變量不會進行變量提升,變量的作用域的開始到變量聲明之前的位置成為臨時死區,當在該區域使用變量時回拋出異常,即使是使用相對安全的typeof操作符。
typeof a;//作用域外不會拋出異常
function f(){ // typeof a;//ReferenceError: a is not defined
if(true) { ; } let a=10; } f();
四、循環中的函數
在循環中定義一個函數,打印當前的循環的變量i;
1 var arr=[]; 2 for(var i=0;i<10;i++){ 3 arr.push(function(){ 4 console.log(i); 5 }) 6 } 7 //打印
8 arr.forEach(function(item){ 9 item();//輸出10次10
10 })
為什么輸出結果是10次10呢,因為上面var聲明的便利會提升到循環外面,循環中函數共享(閉包)同一個i。
立即調用調用函數可以解決這個問題。
1 var arr = []; 2 for (var i = 0; i < 10; i++) { 3 arr.push((function (value) { 4 return function () { 5 console.log(value) 6 } 7 })(i)) 8 } 9 //打印
10 arr.forEach(function (item) { 11 item();//0 1 2 ... 8 9
12 })
將i作為行參傳給函數,value會復制i的值,這個之就是內部函數打印的值。
更簡單的解決辦法:使用let使命變量
1 var arr=[]; 2 for(let i=0;i<10;i++){ 3 arr.push(function(){ 4 console.log(i); 5 }) 6 } 7 //打印
8 arr.forEach(function(item){ 9 item();//輸出0 1 ... 8 9
10 })
使用let聲明的變量每次循環都會聲明一個新的變量,所以之前迭代的同名變量初始化j將其初始化,所以每次輸出的值都不一樣。
for-in和for-of也是一樣。
1 var arr=[]; 2 var object={ 3 a:true, 4 b:1, 5 c:2 6 } 7 for(let key in object){ 8 arr.push(function(){ 9 console.log(key) 10 }) 11 } 12 arr.forEach(function(item){ 13 item() //a b c 14 })
使用let會每次聲明一個新的變量保存key,所以會每次輸出不同的值。使用var使用的是同一個變量,最終輸出都是c.
const用在循環中
1 var arr=[]; 2 for(const i=0;i<10;i++){ 3 arr.push(function(){ 4 console.log(i); 5 }) 6 } 7 //打印
8 arr.forEach(function(item){ 9 item();//TypeError: Assignment to constant variable.
10 })
在普通的for循環中使用const聲明變量時,第一次循環const i=0不會報錯,但是當第二次循環中初始化i后,對i執行i++操作時會報錯。所以不能在普通的循環中使用const聲明循環變量。
但是在for-of和for-in循環中使用const不會出現問題,因為只會對新創建的變量初始化,不會去改變變量的值。
1 var arr=[]; 2 var object={ 3 a:true, 4 b:1, 5 c:2
6 } 7 for(const key in object){ 8 arr.push(function(){ 9 console.log(key) 10 }) 11 } 12 arr.forEach(function(item){ 13 item() //a b c
14 })