在javascript中,咱們都知道使用var來聲明變量。javascript是函數級做用域,函數內能夠訪問函數外的變量,函數外不能訪問函數內的變量。javascript
函數級做用域會致使一些問題就是某些代碼塊內的變量會在全局範圍內有效,這咱們是很是熟悉的:java
1 for (var i = 0; i < 10; i++) { 2 console.log(i); // 0,1,2...,9 3 } 4 console.log(i); //10 5 6 if(true){ 7 var s = 20; 8 } 9 console.log(s); //20
在es6中增長了let(變量)和const(常量)來聲明變量,使用的是塊級做用域,變量聲明只在代碼塊內有效:es6
1 for (let i = 0; i < 10; i++) { 2 console.log(i); // 0,1,2...,9 3 } 4 console.log(i); //ReferenceError: i is not defined 5 6 if(true){ 7 let s = 20; 8 } 9 console.log(s); //ReferenceError: s is not defined
咱們最常遇到的循環事件綁定i產生的閉包問題,是由於i綁定在全局,每次訪問都會取到全局的i的值,因此訪問哪一個元素,獲得的i都是循環體最後一輪的i。咱們一般的解決辦法是藉助IIFE。在es6中let定義的i只在循環體內有效,因此每次循環i都是一個新的變量,因而會產生正常的輸出:閉包
1 //使用var 2 var arr1 = []; 3 for (var i = 0; i < 10; i++) { 4 arr1[i] = function() { 5 console.log(i); 6 } 7 } 8 9 arr1[5](); //10 10 arr1[8](); //10 11 12 //使用let 13 var arr2 = []; 14 for (let i = 0; i < 10; i++) { 15 arr2[i] = function() { 16 console.log(i); 17 } 18 } 19 20 arr2[5](); //5 21 arr2[8](); //8
變量提高:ide
在es5中var定義的變量存在變量提高,es6中使用的let不存在變量提高:函數
1 console.log(i); 2 var i = 10; //10 3 4 console.log(s); 5 let s = 20; //ReferenceError: can't access lexical declaration `s' before initialization
函數提高:es5
es5中不管if語句是否執行,在if語句內的函數聲明都會提高到當前的做用域的頂部而獲得執行,es6支持塊級做用域,不管是否進入if,內部的函數都不會影響到外部的函數spa
嚴格模式下函數聲明只能在頂層環境和函數體內,不能if或者循環體內進行函數聲明code
1 //es5環境 2 function f(){ 3 console.log('outside'); 4 } 5 if(true){ 6 function f(){ 7 console.log('inside'); 8 } 9 } 10 11 f(); //inside 12 13 14 //es6環境 15 function d(){ 16 console.log('outside'); 17 } 18 if(true){ 19 function d(){ 20 console.log('inside'); 21 } 22 } 23 24 d(); //outside
每一個代碼塊中即同一個做用域中,每一個變量只能使用let定義一次,使用var重複定義會進行覆蓋,可是使用let重複定義會報錯,不一樣代碼塊能夠重複定義:對象
1 let i = 10; 2 3 if(true){ 4 let i = 20; 5 console.log(i); //20 6 let i = 50; //redeclaration of let i 7 console.log(i); 8 }
暫時性死區:
在代碼塊內,使用let聲明變量以前,該變量都不可用,便是變量在使用賦值以前須要先進行聲明,每一個代碼塊會進行變量綁定,以肯定變量屬於哪一個環境,在綁定以前進行變量的操做都是非法的:
1 let i = 10; 2 3 if(true){ 4 i = 50; //ReferenceError 5 let i = 20; 6 console.log(i); 7 } 8 //由於在if中聲明瞭i,i綁定了if的環境,因此在if的i聲明以前,i都不可用,致使i=50報錯
IIFE有一個重要的做用是封裝變量,避免變量泄漏到全局,塊級做用域的出現,其實就使得IIFE再也不必要了:
1 ~function(){ 2 if(true){ 3 var i = 10; 4 } 5 }() 6 console.log(i); //ReferenceError: i is not defined 7 8 if(true){ 9 let s = 20; 10 } 11 12 console.log(s); //ReferenceError: s is not defined
let和const的區別:
let聲明變量,值能夠改變。const聲明常量,值不可改變(使用const時,常量最好使用大寫)
其餘的屬性let與const一致
1 let a = 10; 2 a = 20; 3 console.log(a); //20 4 5 const B = 30; 6 B = 40; //TypeError: invalid assignment to const `b' 7 console.log(B);
const只保證常量名指向的地址不變,並不能保證地址的數據不變,若是想吧對象凍結數據不可改變,可以使用Object.freeze(obj)
1 const a = {}; 2 a.name = 'gary'; 3 console.log(a.name); //gary 4 5 const b = Object.freeze({}); 6 b.name = 'bob'; 7 console.log(b.name); //undefined
在es5中咱們都知道,在全局定義變量其實就是等於在全局對象設置屬性,全局對象屬性與全局變量是等價的。全局變量的賦值和全局屬性的賦值是同一件事
在es6中修改可這一規定,使用let和const和class聲明的全局變量與全局對象不等價
1 var a = 10; 2 console.log(a === window.a); //true 3 4 let b = 20; 5 console.log(b === window.b); //false