let命令
用來聲明變量,它的用法相似var,可是let命令聲明的變量只在所在的代碼塊中有效。es6
{ var a = 1; let b = 2; } console.log(a); // 1 console.log(b); // b is not defined b未定義
這就說明let定義的變量只在對應的代碼塊中有效。
一樣的下面是個for循環數組
for(let i = 0; i < 5; i++){ // ... } console.log(i);// i is not defined i未定義
i只在循環體內有效。
阮一峯老師的文章中有一個例子咱們看一下而且分析一下瀏覽器
var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 6
老師給咱們的分析是這樣子的:數據結構
上面代碼中,變量i是let聲明的,當前的i只在本輪循環有效,因此每一次循環的i其實都是一個新的變量,因此最後輸出的是6。你可能會問,若是每一輪循環的變量i都是從新聲明的,那它怎麼知道上一輪循環的值,從而計算出本輪循環的值?這是由於 JavaScript 引擎內部會記住上一輪循環的值,初始化本輪的變量i時,就在上一輪循環的基礎上進行計算。
若是還不理解的話咱們能夠再看一個例子:
var a = []; for (let i = 0; i < 10; i++) { let val = i; a[i] = function () { console.log(val); }; } a[6](); // 6
這時候你就會理解緣由了,就至關於for循環中的執行體,每次的執行都位於一個單獨的代碼塊中。這個val是咱們顯示定義的一個副本,當咱們執行a[6]的時候,他會找到a[6]保存的函數的執行環境下去找這個val,數組a中的每一個元素都位於一個單獨的代碼塊中互不影響。那麼咱們能夠得出一個結論:js引擎會爲咱們for循環中每次循環的代碼塊保存一個副本。Nicholas C. Zakas
的《深刻理解ES6》
中也有相關解釋。
不存在變量提高ide
console.log(a);// undefined console.log(b);// b is not defined var a = 1; let b = 2;
暫時性死區
只要塊級做用域中存在let命令,那麼let聲明的變量就會綁定這個做用域不受外部影響。函數
var temp = 'hello'; if(true){ console.log(temp);// temp is not defined let temp; } 緣由就是let綁定了塊級做用域而且let定義的變量不會提高,就是這個地盤跟我同名的都得死。 隱蔽的死區 function test(x = y, y = 2) { return [x, y]; } test();// 報錯
緣由是:當執行test()的時候會先將y的值賦給x做爲初始值,但此時y不存在,因此報錯。es5
function test(x = 2, y = x) { return [x, y]; } test();// [2, 2]
這樣就沒問題了。指針
總之,暫時性死區的本質就是,只要一進入當前做用域,所要使用的變量就已經存在了,可是不可獲取,只有等到聲明變量的那一行代碼出現,才能夠獲取和使用該變量。
不容許重複聲明
let不容許在同一做用域聲明兩個相同的變量。code
let a; var a;//報錯 a已經被聲明瞭
function fun(params) { let params; } func();//報錯 params已經被聲明瞭 function test(params) { {//內部代碼塊 let params; } } test();//正常執行 執行函數內部的代碼塊被其中的那個變量佔用了而已
塊級做用域
先看一下es5,es5只有全局做用域和函數做用域。對象
var name = 'Jason'; function test() { console.log(name); if(false) { var name = 'Nico'; } } ==>至關於 var name = 'Jason'; function test() { var name;//變量提高 console.log(name); if(false) { name = 'Nico'; } } 因此執行test()會打印undefined
還有就是for循環的時候i的定義被提早
for(var i = 0; i < 3; i++) { console.log(i); } console.log(i); 依次打印0 1 2 3
緣由是i定義被提早,以下:
var i; for(i = 0; i < 3; i++) { console.log(i); } console.log(i);
es6出現了塊級做用域
function test() { let n = 0; if(true) { let n = 1; } console.log(n); } test();// 0
外層的塊級做用域不受內層的控制。
{ { let a = 1; } console.log(a);//報錯 a未被定義 }
外層做用域沒法訪問內層做用域定義的變量
{ let a = 0; { let a = 1; } }
內層做用域能夠命名外層已經命名的變量。
值得提醒的是函數在塊級做用域中的聲明
function test() { console.log('outside'); } (function() { if(false) { function test() { console.log('inside'); } } console.log(test()); })();
ES5會打印inside字符串,ES6卻會報test is not a function的錯。
是否是很詫異,爲何ES6沒有打印outside呢?
緣由是:ES6中容許塊級做用域中聲明函數,可是函數聲明會相似於var提高到全局做用域或者函數做用域頭部,同時函數聲明會提高到塊級做用域頭部,因此上面的代碼就至關於
function test() { console.log('outside'); } (function() { var test = undefined; if(false) { function test() { console.log('inside'); } } console.log(test()); })(); 因此纔會報test is not a function的錯誤。
因此儘可能避免塊級做用域中聲明函數,若是有必要的話可使用函數表達式。
function test() { console.log('outside'); } (function() { if(false) { let test = function() { console.log('inside'); } } console.log(test());// outside })();
const命令
用於定義常量,一旦定義必須馬上初始化否則報錯,定義後沒法改變值,改變也會報錯。
const USER_NAME;//報錯
const USER_ID = '410100'; USER_ID = 2;//報錯
const的做用域與let命令相同:只在聲明所在的塊級做用域內有效
const命令聲明的常量也是不提高,一樣存在暫時性死區,只能在聲明的位置後面使用。
const聲明的常量,也與let同樣不可重複聲明。
const其實保證的是變量存儲的內存地址不得改動,對於簡單的數據類型(數值,布爾,字符串),值就存在內存地址指向的位置,就等同於常量。可是對於數組、對象保存的就是一個指針,只能保證這個指針不被改變。對於內部的數據結構的變化是沒法控制的。
const ARR = []; ARR.push(1);// 能夠執行 ARR.length = 0;// 能夠執行 ARR = [];//報錯
const OBJ = {}; OBJ.name = 'JASON';// 能夠執行 OBJ = {};//報錯
ES6 聲明變量的六種方法:var
function
let
const
import
class
頂層對象:瀏覽器中指window Node中指global
ES5的時候全局變量的賦值與頂層變量的賦值是同一件事。
ES6改變了這一點除了var
與function
還保持原來的方式,新的聲明變量的方法再也不與頂層變量掛鉤。