最近作項目的過程當中,使用到了ES6,由於以前不多接觸,因此使用起來還不夠熟悉。所以購買了阮一峯老師的ES6標準入門,在此感謝阮一峯老師的著做。javascript
咱們知道,ECMAScript 6即ES6是ECMAScript的第五個版本,由於在2015年6月正式發佈,因此又成爲ECMAScript2015。ES6的主要目的是爲了是JS用於編寫複雜的大型應用程序,成爲企業級的開發語言。html
說明:因爲有時候咱們但願得知es6代碼的具體實現原理或者說但願可以轉化爲es5使用,咱們可使用http://babeljs.io/來實如今線將es6代碼轉化爲es5代碼。java
咱們知道,在javascript中只有全局做用域和函數做用域,並不存在塊級做用域。這樣,在使用時就會出現一些問題。 下面咱們先來舉例說明let塊級做用域的使用。es6
例1:數組
代碼以下所示: 安全
{ var a=5; let b=10; } console.log(a); console.log(b);
咱們在控制檯獲得的結果以下所示:babel
也就是說,var聲明的變量因爲不存在塊級做用域因此能夠在全局環境中調用,而let聲明的變量因爲存在塊級做用域因此不能在全局環境中調用。閉包
例2:這個例子是一個很是經典的例子。函數
var a=[]; for(var i=0;i<10;i++){ a[i]=function(){ console.log(i); }; } a[6](); //10
var a=[]; for(let i=0;i<10;i++){ a[i]=function(){ console.log(i); }; } a[6](); //6
咱們能夠看到,兩個例子中,惟一的區別是前者for循環中使用var來定義i,獲得的結果是10.然後者使用的是let來定義i,最終獲得的結果是6.這是爲何呢?阮一峯老師在書中的解釋並非很清楚,因此下面我會發表我的看法:spa
關於這個問題,表面上確實不是很好理解,查詢了不少資料,許多人講到了不少晦澀難懂的知識,彷佛很高大上,可是實際上並不難,下面根據個人理解進行解釋,若有問題,歡迎批評指正,若是你們可以有些收穫就再好不過了。
例二前者(var i)具體執行過程以下:
var a=[];
var i=0;//因爲var來聲明變量i,因此for循環代碼塊不具有塊級做用域,所以i認爲是全局變量,直接放在全局變量中。
a[0]=function(){
console.log(i);//這裏之因此i爲i而不是0;是由於咱們只是定義了該函數,未被調用,因此沒有進入該函數執行環境,i固然不會沿着做用域鏈向上搜索找到i的值。
}// 因爲不具有塊級做用域,因此該函數定義就是全局做用域。
var i=1;//第二次循環,這時var i=1;覆蓋了前面的var i=0;即如今i爲1;
a[1]=function(){
console.log(i);//解釋同a[0]函數。
}
var i=2;// 第三次循環,這時 i=2,在全局做用域中,因此覆蓋了前面的i=1;
a[2]=function(){
console.log(i);
}
......第四次循環 此時i=3 這個以及下面的i不斷的覆蓋前面的i,由於都在全局做用域中
......第五次循環 此時i=4
......第六次循環 此時i=5
......第七次循環 此時i=6
......第八次循環 此時i=7
......第九次循環 此時i=8
var i=9;
a[9]=function(){
console.log(i);
}
var i=10;// 這時i爲10,由於不知足循環條件,因此中止循環。
緊接着在全局環境中繼續向下執行。
a[6]();//這時調用a[6]函數,因此這時隨即進入a[6]函數的執行環境,即a[6]=function(){console.log(i)};執行函數中的代碼 console.log(i); 由於在函數執行環境中不存在變量i,因此此時會沿着做用域鏈向上尋找(可參考個人博文《深刻理解做用域和做用域鏈》),即進入了全局做用域中尋找變量i,而全局做用域中i=10覆蓋了前面全部的i值,因此說這時i爲10,那麼a[6]的值就是10了。
說明:對於例如a[1]=function(){console.log(i)};而不是a[1]=function{console.log(1)},能夠在控制檯中輸出a[1]函數,便可獲得驗證。
例二後者(let i)具體執行過程以下:
var a=[];//建立一個數組a;
{ //進入第一次循環
let i=0; //注意:由於使用let使得for循環爲塊級做用域,這次let i=0在這個塊級做用域中,而不是在全局環境中。
a[0]=function(){
console.log(i);
}; //注意:因爲循環時,let聲明i,因此整個塊是塊級做用域,那麼a[0]這個函數就成了一個閉包。
}// 聲明: 我這裏用{}表達並不符合語法,只是但願經過它來講明let存在時,這個for循環塊是塊級做用域,而不是全局做用域。
講道理,上面這是一個塊級做用域,就像函數做用域同樣,函數執行完畢,其中的變量會被銷燬,可是由於這個代碼塊中存在一個閉包,閉包的做用域鏈中包含着(或着說是引用着)塊級做用域,因此在閉包被調用以前,這個塊級做用域內部的變量不會被銷燬。(更多閉包知識,能夠看個人博文《JavaScript之閉包》)
{ //進入第二次循環
let i=1; //注意:由於let i=1; 和 上面的let i=0;出在不一樣的做用域中,因此二者不會相互影響。
a[1]=function(){
console.log(i);
}; //一樣,這個a[i]也是一個閉包
}
......進入第三次循環,此時其中let i=2;
......進入第四次循環,此時其中let i=3;
......進入第五次循環,此時其中let i=4;
......進入第六次循環,此時其中let i=5;
......進入第七次循環,此時其中let i=6;
......進入第八次循環,此時其中let i=7;
......進入第九次循環,此時其中let i=8;
{//進入第十次循環
let i=9;
a[i]=function(){
console.log(i);
};//一樣,這個a[i]也是一個閉包
}
{
let i=10;//不符合條件,再也不向下執行。因而這個代碼塊中不存在閉包,let i=10;在此次循環結束以後難逃厄運,隨即被銷燬。
}
a[6]();//調用a[6]()函數,這時執行環境隨即進入下面這個代碼塊中的執行環境:funcion(){console.log(i)};
{
let i=6;
a[6]=function(){
console.log(i);
}; //一樣,這個a[i]也是一個閉包
}
a[6]函數(閉包)這個執行環境中,它會首先尋找該執行環境中是否存在 i,沒有找到,就沿着做用域鏈繼續向上到了其所在的代碼塊執行環境,找到了i=6,因而輸出了6,即a[6]();的結果爲6。這時,閉包被調用,因此整個代碼塊中的變量i和函數a[6]()被銷燬。
相信你們仔細看完上面的函數執行的過程,對let var 塊級做用域 閉包就有一個很好的理解了。我認爲重要的是對於函數執行過程的理解!
這裏是說使用let不會像使用var同樣存在一個變量提高的現象。變量提高是什麼呢?在沒有接觸es6以前我對此也不清楚,可是我想你們必定都據說過函數聲明提高:函數聲明來定義函數便可實現函數聲明提高,這樣,咱們能夠先調用函數,後聲明函數;而函數表達式方法不會實現函數聲明提高,這樣,若是先調用函數,後聲明函數,則會拋出錯誤!!(對於函數聲明提高更多知識能夠看個人博文《JavaScript函數之美~》)。 那麼能夠以此類推,var定義變量:能夠先使用,後聲明;而let定義變量:只可先聲明,後使用。
例3:
var num1=100; console.log(num1); let num2=200; console.log(num2); console.log(i); var i=10; console.log(j); let j=5;
咱們能夠看到結果以下:
即前兩個都是先聲明後使用,沒有問題。然後兩個都是先使用,後聲明,用var 聲明的顯示undefined,而 let聲明的直接報錯。
說明:console.log(i);
var i=10;
實際上至關於:
var i;
console.log(i);
i=10;
因此會出現undefined的狀況。
暫時性死區即:只要一進入當前做用域,所要使用的變量就已經存在,可是不可獲取,只有等到聲明變量的那一行代碼出現,才能夠獲取和使用該變量。
例5:
var tmp=123; if(true){ tmp="abc"; let tmp; }
結果以下:
也就是說:雖然上面的代碼中存在全局變量tmp,可是塊級做用域內let又聲明瞭一個局部變量tmp,致使後者綁定了塊級做用域,因此在let聲明變量前,對tmp賦值會報錯。此即暫時性死區。
注意:ES6規定暫時性死區和不存在變量提高就是爲了減小運行時的錯誤,防止在變量聲明前就使用這個變量,從而致使意料以外的行爲。
暫時性死區就是: 只要塊級做用域內存在let,那麼他所聲明的變量就綁定了這個區域,再也不受外部的影響。
暫時性死區即 Temperary Dead Zone,即TDZ。
注意:暫時性死區也意味着 typeof 再也不是一個百分之百安全的操做。 以下:
if (true) { console.log(typeof x); let x; }
這裏若是沒有let x,那麼typeof x的結果是 undefined,可是若是使用了let x,由於let不存在變量提高,因此這裏造成了暫時性死區,即typeof x也是會報錯的。。。 從這裏能夠理解暫時性死區實際上就是這一部分是有問題的 。
四.不容許重複聲明
function func (){ let b=100; var b=10; } function add(num){ let num; return num+1; } function another(){ let a=10; let a=5; }
上述三個獲得的結果均爲:
只是前二者爲 b和num被聲明過了。注意:第二個函數,雖然咱們沒有明確的聲明,可是參數其實是至關於用var聲明的局部變量。
什麼使const命令呢?實際上它也是一種聲明常量的方式。const命令用來聲明常量,一旦聲明,其值就不能改變。初次以外,const和let十分類似。也就是說前者是用於聲明常量的,後者是用於聲明變量的。
1.const聲明常量,一旦聲明,不可改變。
const a=10;
a=100;
結果以下
2.既然const一旦聲明不可改變,因此在聲明時必須初始化。
const a;
結果以下:
3.const所在的代碼塊爲塊級做用域,因此其變量只在塊級做用域內使用或其中的閉包使用。
if(true){ const a=10; } console.log(a);
結果以下:
4.const聲明的變量不存在變量提高。
if(true){ console.log(a); const a=10; }
結果以下:
5.const不可重複聲明常量。
var a=10; const a=5;
結果以下:
6.const命令只是保證了變量名指向的地址不變,並不保證該地址的數據不變。
const a={}; a.name="zzw"; console.log(a.name); const b=[]; b.push("zzw"); console.log(b); const c={}; c={name:"zzw"};
結果以下:
所以,咱們使用const所指向的地址不可變,可是地址的內容是能夠變得。
7.若是但願將對象自己凍結,可使用Object.freeze()方法。
const a=Object.freeze({}); a.name="zzw"; console.log(a.name); //undefined
因而經過Object.freeze()方法咱們就不能夠再改變對象的屬性了(無效)。