1. 程式人生 > 實用技巧 >ES-Ver:ECMAScript 6

ES-Ver:ECMAScript 6

ylbtech-ES-Ver:ECMAScript 6
ECMAScript6(簡稱ES6)是於2015年6月正式釋出的JavaScript語言的標準,正式名為ECMAScript 2015(ES2015)。它的目標是使得JavaScript語言可以用來編寫複雜的大型應用程式,成為企業級開發語言 另外,一些情況下ES6也泛指ES2015及之後的新增特性,雖然之後的版本應當稱為ES7、ES8等。
1.返回頂部
1、
外文名:ECMAScript 6
簡稱:ES6
型別:前端語言
通過日期:2015年6月
正式名字:ECMAScript 2015(ES2015)
性質:JavaScript語言的標準

目錄

  1. 1發展歷史
  2. 2新增功能
  3. 宣告命令
  4. 解構賦值
  1. 字元處理
  2. 3效能擴充套件
  3. 字串
  4. 遍歷器
  1. 正則表示式
  2. 4相容問題
2、
2.返回頂部
1、

發展歷史

2000年,ECMAScript 4.0開始醞釀。這個版本最後沒有通過,但是它的大部分內容被ECMAScript6繼承了。因此,ECMAScript6制定的起點其實是2000年。 2007年10月,ECMAScript 4.0草案發布,本來預計2008年8月釋出正式版本。但是,各方對於是否通過這個標準,發生了嚴重分歧。以
Yahoo
MicrosoftGoogle為首的大公司,反對JavaScript的大幅升級,主張小幅改動;以JavaScript創造者Brendan Eich為首的Mozilla公司,則堅持當前的草案。 2008年7月,由於對於下一個版本應該包括哪些功能,各方分歧太大,爭論過於激烈,ECMA開會決定,中止ECMAScript 4.0的開發,將其中涉及現有功能改善的一小部分,釋出為ECMAScript 3.1,而將其他激進的設想擴大範圍,放入以後的版本,由於會議的氣氛,該版本的專案代號起名為Harmony(和諧)。會後不久,ECMAScript 3.1就改名為ECMAScript 5。 2009年12月,ECMAScript 5.0正式釋出。Harmony專案則一分為二,一些較為可行的設想定名為 JavaScript.next繼續開發,後來演變成ECMAScript 6;一些不是很成熟的設想,則被視為JavaScript.next.next,在更遠的將來再考慮推出。 2011年,ECMAScript 5.1釋出後開始6.0版的制定。 2013年3月,ECMAScript 6草案凍結,不再新增新功能。新的功能設想將被放到ECMAScript 7。 2013年12月,ECMAScript 6草案發布。然後是12個月的討論期,聽取各方反饋。由於這個版本引入的語法功能太多,而且制定過程當中,還有很多組織和個人不斷提交新功能。標準委員會最終決定,標準在每年的6月份正式釋出一次,作為當年的正式版本。接下來的時間,就在這個版本的基礎上做改動,直到下一年的6月份,草案就自然變成了新一年的版本。 2015年6月,ECMAScript 6(ES6)正式通過,成為國際標準,正式名稱是“ECMAScript 2015”(簡稱ES2015)。 2016年6月,小幅修訂的“ECMAScript 2016”(簡稱ES2016或ES7)標準釋出,相當於ES6.1版,因為兩者的差異非常小(只新增了
陣列
例項的includes方法和指數運算子),基本上是同一個標準

新增功能

宣告命令

1.let命令 ES6新增了let命令,用來宣告變數。它的用法類似於var,但是所宣告的變數,只在let命令所在的程式碼塊內有效。下面程式碼在程式碼塊之中,分別用let和var聲明瞭兩個變數。然後在程式碼塊之外呼叫這兩個變數,結果let宣告的變數報錯,var宣告的變數返回了正確的值。這表明,let宣告的變數只在它所在的程式碼塊有效。
1 2 3 4 5 6 { leta=10; varb=1; } console.log(b);//1 console.log(a);//報錯a沒有定義
for迴圈的計數器,就很合適使用let命令,計數器i只在for迴圈體內有效,在迴圈體外引用就會報錯。 下面的程式碼如果使用var,最後輸出的是10。因為變數i是var命令宣告的,在全域性範圍內都有效,每一次迴圈,變數i的值都會發生改變,而迴圈內被賦給陣列a的函式內部的console.log(i),裡面的i指向的就是全域性的i。也就是說,所有陣列a的成員裡面的i,指向的都是同一個i,導致執行時輸出的是最後一輪的i的值,也就是10。 如果使用let,宣告的變數僅在塊級作用域內有效,最後輸出的是6。因為變數i是let宣告的,當前的i只在本輪迴圈有效,所以每一次迴圈的i其實都是一個新的變數,所以最後輸出的是6。由於JavaScript引擎內部會記住上一輪迴圈的值,初始化本輪的變數i時,就在上一輪迴圈的基礎上進行計算:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 vara=[]; for(vari=0;i<10;i++){ a[i]=function(){ console.log(i); }; } a[6]();//10 vara=[]; for(leti=0;i<10;i++){ a[i]=function(){ console.log(i); }; } a[6]();//6
let不允許在相同作用域內,重複宣告同一個變數,不能在函式內部相同模組範圍重新宣告引數。
1 2 3 4 5 6 7 8 9 10 11 12 leta=10;//即使宣告是vara=10;後面一樣報錯 leta=1;//報錯 functionfunc(arg){ letarg;//呼叫時因同範圍重名報錯 } functionfunc(arg){ { letarg;//不報錯,因為對上一個arg來看在子模組中 } }
另外,for迴圈還有一個特別之處,就是設定迴圈變數的那部分是一個單獨的父作用域,而迴圈體內部是子作用域
1 2 3 4 5 6 7 8 9 10 11 leti=123; console.log(i); for(leti=0;i<2;i++,console.log(i)){ leti='abc'; console.log(i); } //123 //abc //1 //abc //2
var命令會發生“變數提升”現象,即變數可以在宣告之前使用,值為undefined。為了糾正這種現象,let命令改變了語法行為,它所宣告的變數一定要在聲明後使用,否則報錯。這在語法上,稱為“暫時性死區”(temporal dead zone,簡稱TDZ)。例如:在let x前增加一句typeof x就會報錯,因為同一塊作用域在let x之前,x無法進行任何操作。 let實際上為JavaScript新增了塊級作用域,在{}被包圍的範圍外,不受內層的let變數影響(但會受var的“變數提升”影響):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 functiontext1(){ letn=5;//或varn=5; if(true){ letn=10; } console.log(n);//5 } functiontext2(){ varn=5; if(true){ varn=10; } console.log(n);//10 } functiontext3(){ letn=5; if(true){ varn=10;//報錯,已經聲明瞭n } }
2. const命令 const宣告一個只讀的常量。一旦宣告,常量的值就不能改變,且宣告時必須立即初始化,不能留到以後賦值。const的作用域與let命令相同:只在宣告所在的塊級作用域內有效。 const實際上保證的,並不是變數的值不得改動,而是變數指向的那個記憶體地址不得改動。對於簡單型別的資料(數值、字串、布林值),值就儲存在變數指向的那個記憶體地址,因此等同於常量。但對於複合型別的資料(主要是物件和陣列),變數指向的記憶體地址,儲存的只是一個指標,const只能保證這個指標是固定的,至於它指向的資料結構是不是可變的,就完全不能控制了。因此,將一個物件宣告為常量必須非常小心。
1 2 3 4 constfoo={};//constfoo=[]同理,可以正常使用push等功能 foo.prop=123;//為foo新增一個屬性,可以成功 console.log(foo.prop);//123 foo={};//將foo指向另一個物件,就會報錯
如果真的想將物件或物件屬性凍結,應該使用Object.freeze方法 3.Class命令 ES6 提供了更接近傳統語言的寫法,引入了Class(類)這個概念(類的資料型別就是函式,類本身就指向建構函式),作為物件的模板。通過class關鍵字,可以定義類。class可以看作只是一個語法糖,它的絕大部分功能,ES5都可以做到,新的class寫法只是讓物件原型的寫法更加清晰、更像面向物件程式設計的語法而已:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 functionPoint(x,y){ this.x=x; this.y=y; } Point.prototype.toString=function(){ return'('+this.x+','+this.y+')'; }; //上面為原先寫法,下面為ES6的Class寫法 classPoint{ constructor(x,y){//構造方法,this關鍵字代表例項物件 this.x=x; this.y=y; } toString(){//自定義方法,方法之間不需要逗號分隔,加了會報錯 return'('+this.x+','+this.y+')'; } }
建構函式的prototype屬性,在ES6的類上面繼續存在。事實上,類的所有方法都定義在類的prototype屬性上面。但類的內部所有定義的方法,都是不可列舉的(non-enumerable)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 classPoint{ constructor(){ } toString(){ } toValue(){ } } console.log(Object.keys(Point.prototype));//[]不可列舉 console.log(Object.getOwnPropertyNames(Point.prototype));//["constructor","toString","toValue"] //相當於 functionPoint(){ } Point.prototype={ constructor(){}, toString(){}, toValue(){}, }; console.log(Object.keys(Point.prototype));//["constructor","toString","toValue"]
類的屬性名,可以採用表示式:
1 2 3 4 5 letmethodName='getArea'; classSquare{ [methodName](){ } }
與函式一樣,類也可以使用表示式的形式定義。下面程式碼使用表示式定義了一個類。需要注意的是,這個類的名字是MyClass而不是Me,Me只在 Class 的內部程式碼可用,指代當前類:
1 2 3 4 5 6 7 8 constMyClass=classMe{//如果類的內部沒用到的話,可以省略Me getClassName(){ returnMe.name; } }; letinst=newMyClass(); console.log(inst.getClassName())//Me Me.name//報錯,Me沒有定義
類相當於例項的原型,所有在類中定義的方法,都會被例項繼承。如果在一個方法前,加上static關鍵字,就表示該方法不會被例項繼承而是直接通過類來呼叫,這就稱為“靜態方法”
1 2 3 4 5 6 7 8 classFoo{ staticclassMethod(){ return'hello'; } } Foo.classMethod()//'hello' varfoo=newFoo(); foo.classMethod()//報錯foo.classMethod不是一個函式(不存在該方法)
如果靜態方法包含this關鍵字,這個this指的是類,而不是例項。靜態方法可以與非靜態方法重名,父類的靜態方法,可以被子類繼承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 classFoo{ staticbar(){ this.baz();//等同於呼叫Foo.baz } staticbaz(){ console.log('hello'); } baz(){ console.log('world'); } } Foo.bar()//hello classBarextendsFoo{ } Bar.bar()//hello
4.import命令 import雖然屬於宣告命令,但它是和export命令配合使用的。export命令用於規定模組的對外介面,import命令用於輸入其他模組提供的功能一個模組就是一個獨立的檔案。該檔案內部的所有變數,外部無法獲取。如果外部能夠讀取模組內部的某個變數、函式或類,就必須使用export關鍵字輸出。export輸出的變數就是本來的名字,但是可以使用as關鍵字重新命名
1 2 3 4 5 6 7 8 9 10 //profile.js exportvarfirstName='Michael'; exportfunctionf(){}; exportvaryear=1958; //寫法2,與上等同 varfirstName='Michael'; functionf(){}; vary=1958; export{firstName,f,yasyear};
export語句輸出的介面,與其對應的值是動態繫結關係,即通過該介面,可以取到模組內部實時的值。這一點與CommonJS規範完全不同,CommonJS模組輸出的是值的快取。export命令可以出現在模組的任何位置,只要處於模組頂層就可以。如果處於塊級作用域內,就會報錯:
1 2 3 4 5 6 exportvarfoo='bar'; setTimeout(()=>foo='baz',500);//輸出變數foo,值為bar,500毫秒之後變成baz functionfoo(){ exportdefault'bar'//語法錯誤 }
使用export命令定義了模組的對外介面以後,其他JS檔案就可以通過import命令載入這個模組,變數名必需與被匯入模組(profile.js)對外介面的名稱相同。import命令可以使用as關鍵字,將輸入的變數重新命名。除了指定載入某個輸出值,還可以使用整體載入,即用*指定一個物件,所有輸出值都載入在這個物件上面。
1 2 3 4 5 6 import{firstNameasname,f,year}from'./profile.js'; import*aspfrom'./profile.js'; functionsetName(element){ element.textContent=name+''+year;//值等同於p.firstName+''+p.year; }
import命令輸入的變數都是隻讀的,因為它的本質是輸入介面。也就是說,不允許在載入模組的腳本里面,改寫介面。但是,如果是一個物件,改寫物件的屬性是允許的。並且由於import是靜態執行,所以不能使用表示式和變數,這些只有在執行時才能得到結果的語法結構。
1 2 3 4 5 6 7 8 9 10 11 12 import{a}from'./xxx.js';//也可以是絕對路徑,.js字尾可以省略 a.foo='hello';//合法操作 a={};//報錯:a是隻讀的 import{'f'+'oo'}from'/my_module.js';//報錯,語法錯誤(不能用運算子) if(x===1){ import{foo}from'module1';//報錯,語法錯誤(import不能在{}內) }else{ import{foo}from'module2'; }
注意,import命令具有提升效果,會提升到整個模組的頭部,首先執行。import可以不匯入模組中的任何內容,只執行模組中的全域性程式碼。如果多次執行同一模組的import語句,那麼只會執行一次其全域性程式碼,但變數均會正常引入(相當於合併處理)。
1 2 3 4 foo(); import{foo}from'/my_module.js';//不會報錯,因為import的執行早於foo的呼叫 import'/modules/my-module.js';//不引入變數,但執行其中全域性程式碼 import{a}from'/modules/my-module.js';//重複引入不執行全域性程式碼,但引入變數a
除了用大括號引入變數,import還可以直接自定義引入預設變數:
1 2 3 4 5 6 7 8 //export-default.js exportdefaultfunction(){ console.log('foo'); } //import-default.js importcustomNamefrom'./export-default.js';//customName可以是任意名字 customName();//'foo'

解構賦值

ES6允許按照一定模式,從陣列和物件中提取值,對變數進行賦值,這被稱為解構(Destructuring)。本質上,這種寫法屬於“模式匹配”,只要等號兩邊的模式相同,左邊的變數就會被賦予對應的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 leta=1; letb=2; letc=3; //等價於 let[a,b,c]=[1,2,3]; let[,third]=["foo","bar","baz"]; third//"bar" let[head,...tail]=[1,2,3,4]; head//1 tail//[2,3,4] let[x,y,...z]=['a']; x//"a" y//變數解構不成功,賦值為undefined z//陣列解構不成功,賦值為[]
解構賦值允許指定預設值只有當一個數組成員嚴格等於undefined,預設值才會生效
1 2 3 4 let[foo=true]=[];//foo=true let[x,y='b']=['a'];//x='a',y='b' let[q=1,w='b']=['a',undefined];//q='a',w='b' let[e=1]=[null];//e=null
解構不僅可以用於陣列,還可以用於物件。物件的解構與陣列有一個重要的不同。陣列的元素是按次序排列的,變數的取值由它的位置決定而物件的屬性沒有次序,變數必須與屬性同名,才能取到正確的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 let{bar,foo}={foo:"aaa",bar:"bbb"}; foo//"aaa" bar//"bbb" let{abc}={foo:"aaa",bar:"bbb"}; abc//undefined let{foo:baz}={foo:'aaa',bar:'bbb'}; baz//"aaa" constnode={ loc:{ start:{ line:1, column:5 } } }; let{loc,loc:{start},loc:{start:{line}}}=node; line//1 loc//Object{start:Object} start//Object{line:1,column:5}
解構賦值用途示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 //交換變數的值 letx=1; lety=2; [x,y]=[y,x]; //提取JSON資料 letjsonData={ id:42, status:"OK", data:[867,5309] }; let{id,status,data:number}=jsonData; console.log(id,status,number);//42,"OK",[867,5309] //遍歷Map結構 constmap=newMap(); map.set('first','hello'); map.set('second','world'); for(let[key,value]ofmap){ console.log(key+"is"+value); } //firstishello //secondisworld

字元處理

ES6 又提供了三種新方法用來確定一個字串是否包含在另一個字串中,而且這三個方法都支援第二個引數,表示開始搜尋的位置:
  • includes():返回布林值,表示是否找到了引數字串。
  • startsWith():返回布林值,表示引數字串是否在原字串的頭部。
  • endsWith():返回布林值,表示引數字串是否在原字串的尾部。
repeat方法返回一個新字串,表示將原字串重複n次。引數如果是小數,會被取整。引數是負數或者Infinity,會報錯。但是,如果引數是 0 到-1 之間的小數,則等同於 0,這是因為會先進行取整運算。0 到-1 之間的小數,取整以後等於-0,repeat視同為0。 傳統的 JavaScript 語言,輸出模板通常是字串與變數相互交錯累加而成,寫法相當繁瑣不方便,ES6 引入了模板字串解決這個問題。模板字串(template string)是增強版的字串,用反引號(`)標識。它可以當作普通字串使用,也可以用來定義多行字串,或者在字串中嵌入變數。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 //傳統寫法 alert( '<div>Thereare<b>'+basket.count+'</b>'+ 'itemsinyourbasket,'+ '<em>'+basket.onSale+ '</em>areonsale!</div>' ); //模板字串,換行、空格、縮排均會保留 alert(` <div>Thereare<b>${basket.count}</b>items inyourbasket,<em>${basket.onSale}</em> areonsale!</div> `);
大括號內部可以放入任意的JavaScript表示式,可以進行運算,還能呼叫函式,甚至還能巢狀。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 consttmpl=addrs=>` <table> ${addrs.map(addr=>` <tr><td>${addr.first}</td></tr> <tr><td>${addr.last}</td></tr> `).join('')} </table> `; constdata=[ {first:'<Jane>',last:'Bond'}, {first:'Lars',last:'<Croft>'}, ]; console.log(tmpl(data)); //<table> // //<tr><td><Jane></td></tr> //<tr><td>Bond</td></tr> // //<tr><td>Lars</td></tr> //<tr><td><Croft></td></tr> // //</table>
將模板字串放於函式名後面,該函式將被呼叫來處理這個模板字串,被稱為標籤模板功能(tagged template)。標籤模板其實不是模板,而是函式呼叫的一種特殊形式。它的解析方式非常特別,當字串中含有變數時,解析後的第一個引數將會是陣列,而陣列內各項值,由模板中最前面的字串+每個變數之後的字串組成。
1 2 3 4 5 leta=5; letb=10; console.log`Hello${a+b}world${a*b}`; //等同於 console.log(['Hello','world',''],15,50);

效能擴充套件

字串

之前的JavaScript只能識別“\u0000”~“\uFFFF”之間的Unicode碼字元。超出這個範圍的字元,必須用兩個雙位元組的形式表示,所以會把“\u20BB7”理解成\u20BB+7,只會顯示一個空格(\u20BB是一個不可列印字元),後面跟著一個7。ES6對這一點做出了改進,只要將碼點放入大括號,就能正確解讀該字元。“\u{20BB7}”解讀為“