ECMAScript 6(以下簡(jiǎn)稱(chēng)ES6)是JavaScript語(yǔ)言的下一代標(biāo)準(zhǔn)由捎。因?yàn)楫?dāng)前版本的ES6是在2015年發(fā)布的仿耽,所以又稱(chēng)ECMAScript 2015娄蔼。也就是說(shuō)赖条,ES6就是ES2015。
說(shuō)明:此文章根據(jù)《實(shí)戰(zhàn)ES2015:深入現(xiàn)代JavaScript+應(yīng)用開(kāi)發(fā)》這本書(shū)做的筆記宪哩,更多詳細(xì)內(nèi)容請(qǐng)查看書(shū)籍娩贷。電子版在文章底部。
一锁孟、ECMAScript的發(fā)展歷程
二彬祖、ES2015能為實(shí)際開(kāi)發(fā)帶來(lái)什么
ECMAScript的發(fā)展速度在不斷加快,影響范圍越來(lái)越大品抽,除了Web前端開(kāi)發(fā)以外储笑,借助著Node.js的力量在服務(wù)器、桌面端甚至硬件設(shè)備等領(lǐng)域中也發(fā)光發(fā)熱著圆恤。
ES2015概述:
ES2015標(biāo)注提供了許多新的語(yǔ)法和編程特性以提高ECMAScript的開(kāi)發(fā)效率并優(yōu)化ECMAScript的開(kāi)發(fā)體驗(yàn)南蓬。
ES2015的別名:Harmony(和諧)
語(yǔ)法糖:
ECMAScript帶來(lái)了可用性非常高的語(yǔ)法糖,這些語(yǔ)法糖的開(kāi)發(fā)初衷是方便開(kāi)發(fā)者使用哑了,使用語(yǔ)法糖能夠增加程序的可讀性赘方,從而減少程序代碼出錯(cuò)的幾率。
如ES2015中非常重要的箭頭函數(shù)弱左,大大地增強(qiáng)了ECMAScript在復(fù)雜業(yè)務(wù)邏輯中的處理能力窄陡。
使用ES2015前:
el.on('click',function(evt) {
var self = this;
fetch('/api').then(function (res) {
return res.json();
}).then(function (result) {
self.something(result);
//...
})
})
使用ES2015后:
el.on('click',evt=>{
fetch('/api').then(res=>res.json()).then(result=>this.something(result))
})
模塊化和組件化:
在程序代碼可以通過(guò)模塊化進(jìn)行解耦后,組件化開(kāi)發(fā)便能借此進(jìn)一步推進(jìn)項(xiàng)目程序的工程化進(jìn)度拆火。組件化開(kāi)發(fā)是模塊化開(kāi)發(fā)的高級(jí)體現(xiàn)跳夭,組件化更能表現(xiàn)出模塊化開(kāi)發(fā)的意義和重要性。
組件化開(kāi)發(fā)所重視的是組件之間的非耦合關(guān)系和組件的可重用性们镜,組件之間也可以存在依賴(lài)性币叹,可以利用模塊化來(lái)實(shí)現(xiàn)組件化開(kāi)發(fā)。同一類(lèi)內(nèi)容塊可以抽象化為一個(gè)組件模狭,并在生產(chǎn)中重復(fù)使用颈抚。
const和let:
const為ECMAScript帶來(lái)了定義常量的能力,let為ECMAScript修復(fù)了從前var因?yàn)榇a習(xí)慣不佳而導(dǎo)致的代碼作用域混亂等問(wèn)題嚼鹉,同時(shí)實(shí)現(xiàn)了塊狀作用域贩汉。
const可以實(shí)現(xiàn)變量名與內(nèi)存地址的強(qiáng)綁定驱富,讓變量不會(huì)因?yàn)槌硕x語(yǔ)句和刪除語(yǔ)句以外的代碼而丟失內(nèi)存地址的綁定,從而保證了變量與內(nèi)存之間的安全性匹舞。
總結(jié):語(yǔ)法糖褐鸥、模塊化、組件化等工程優(yōu)勢(shì)赐稽,可以在實(shí)際開(kāi)發(fā)中提升開(kāi)發(fā)效率和代碼質(zhì)量叫榕。
三、ES2015新語(yǔ)法
新語(yǔ)法:
- let姊舵、const和塊級(jí)作用域
- 箭頭函數(shù)(Arrow Function)
- 模板字符串(Template String)
- 對(duì)象字面量擴(kuò)展語(yǔ)法(Enhanced Object Literals)
- 表達(dá)式結(jié)構(gòu)(Destructuring)
- 函數(shù)參數(shù)表達(dá)晰绎、傳參
- 新的數(shù)據(jù)結(jié)構(gòu)
- 類(lèi)語(yǔ)法(Classes)
- 生成器(Generator)
- Promise
- 代碼模塊化
- Symbol
- Proxy
let、const和作用域
let和const是繼var之后新的變量定義方法蠢莺,與let相比,const更容易被理解零如。const就是constant的縮寫(xiě)躏将,用于定義變量,即不可變量考蕾。const定義常量的原理是阻隔變量名所對(duì)應(yīng)的內(nèi)存地址被改變祸憋。
變量與內(nèi)存之間的關(guān)系由三個(gè)部分組成:變量名、內(nèi)存綁定和內(nèi)存(內(nèi)存地址)肖卧。
ECMAScript在對(duì)變量的引用進(jìn)行讀取時(shí)蚯窥,會(huì)從該變量對(duì)應(yīng)的內(nèi)存地址所指向的內(nèi)存空間中讀取內(nèi)容。當(dāng)用戶改變變量的值時(shí)塞帐,引擎會(huì)重新從內(nèi)存中分配一個(gè)新的內(nèi)存空間以存儲(chǔ)新的值拦赠,并將新的內(nèi)存地址與變量進(jìn)行綁定。const的原理便是在變量名與內(nèi)存地址之間建立不可變得綁定葵姥,當(dāng)后面的程序嘗試申請(qǐng)新的內(nèi)存空間時(shí)荷鼠,引擎便會(huì)拋出錯(cuò)誤。
在ES2015中榔幸,let可以說(shuō)是var的進(jìn)化版本允乐,var大部分情況下可以被let替代,let和var的異同點(diǎn)如下表:
變量的生命周期:
在ECMAScript中削咆,一個(gè)變量(或常量)的生命周期(Life Cycle)模式是固定的牍疏,由兩種因素決定,分別是作用域和對(duì)其的引用拨齐。
從工程化的角度鳞陨,應(yīng)該在ES2015中遵從以下三條原則:
(1)一般情況下,使用const來(lái)定義值的存儲(chǔ)容器(常量)瞻惋;
(2)只有在值容器明確地被確定將會(huì)被改變時(shí)才使用let來(lái)定義(變量)炊邦;
(3)不再使用var编矾。
循環(huán)語(yǔ)句:
ECMAScript引入了一種新的循環(huán)語(yǔ)句for...of,主要的用途是代替for...in循環(huán)語(yǔ)句馁害;為Array對(duì)象引入了Array.forEach方法以代替for循環(huán)窄俏,Array.forEach方法的特點(diǎn)是自帶閉包,以解決因?yàn)槿狈K級(jí)作用域?qū)е滦枰褂萌∏傻姆椒▉?lái)解決var的作用域問(wèn)題碘菜。
因?yàn)閴K級(jí)作用域的存在凹蜈,使得for循環(huán)中的每一個(gè)當(dāng)前值可以僅保留在所對(duì)應(yīng)的循環(huán)體中,配合for-of循環(huán)語(yǔ)句更是免去了回調(diào)函數(shù)的使用忍啸。
const arr=[1,2,3];
for(const item of arr){
console.log(item);
}
配合ES2015中的解構(gòu)(Destructuring)特性仰坦,在處理JSON數(shù)據(jù)時(shí),更加得心應(yīng)手计雌。
const Zootopia=[
{name:'Nick',gender:1,species:'Fox'},
{name:'Judy',gender:0,species:'Bunny'}
];
for(const {name,species} of Zootopia){
console.log(`hi,I am ${name},and I am a ${species}`);
}
forEach方法需要傳入一個(gè)回調(diào)函數(shù)來(lái)接收循環(huán)的每一個(gè)循環(huán)元素并作為循環(huán)體以執(zhí)行悄晃。同時(shí),這個(gè)回調(diào)函數(shù)在標(biāo)準(zhǔn)定義中會(huì)被傳入三個(gè)參數(shù)凿滤,分別為:當(dāng)前值妈橄,當(dāng)前值的下標(biāo)和循環(huán)數(shù)組自身。在ES2015標(biāo)準(zhǔn)中翁脆,數(shù)組類(lèi)型再次被賦予了一個(gè)名為entries的方法眷蚓,它可以返回對(duì)應(yīng)的數(shù)組中每一個(gè)元素與其下標(biāo)配對(duì)的一個(gè)新數(shù)組。
這個(gè)新特性可以與解構(gòu)和for-of循環(huán)配合使用反番。
const Zootopia=[
{name:'Nick',gender:1,species:'Fox'},
{name:'Judy',gender:0,species:'Bunny'}
];
for(const [index,{name,species}] of Zootopia.entries){
console.log(`${index}.Hi,I am ${name},and I am a ${species}`);
}
//0.Hi,I am Nick,and I am a Fox
//1.Hi,I am Judy,and I am a Bunny
箭頭函數(shù)
箭頭函數(shù)沙热,顧名思義便是使用箭頭(=>)進(jìn)行定義的函數(shù),屬于匿名函數(shù)(Anonymous Function)一類(lèi)罢缸。
相對(duì)于傳統(tǒng)的function語(yǔ)句篙贸,箭頭函數(shù)在簡(jiǎn)單函數(shù)使用中更為簡(jiǎn)潔直觀。
const arr=[1,2,3];
//箭頭函數(shù)
const squares=arr.map(x=>x*x);
//傳統(tǒng)語(yǔ)法
const squares=arr.map(function(x){return x*x});
箭頭函數(shù)有四種使用語(yǔ)法
(1)單一參數(shù)的單行箭頭函數(shù)
//Syntax:arg=>statement
const fn=foo=>`${foo} world` //means return `foo +' world'`
這是箭頭函數(shù)最簡(jiǎn)潔的形式枫疆,常見(jiàn)于用作簡(jiǎn)單的處理函數(shù)歉秫,如過(guò)濾。
let array=['a','bc','def','ghij'];
array=array.filter(item=>item.length>=2); //bc,def,ghij
(2)多參數(shù)的單行箭頭函數(shù)
//Syntax:(arg1,arg2)=>statement
const fn=(foo,bar)=>foo+bar
多參數(shù)的語(yǔ)法跟普通函數(shù)一樣养铸,以括號(hào)來(lái)包裹參數(shù)列雁芙,這種形式常見(jiàn)于數(shù)組的處理,如排序钞螟。
let array=['a','bc','def','ghij'];
array=array.sort((a,b)=>a.length<b.length); //ghij,def,bc,a
(3)多行箭頭函數(shù)
//Syntax:arg=>{...}
//單一參數(shù)
foo=>{return `${foo} world`}
//Syntax:(arg1,arg2)=>{...}
//多參數(shù)
(foo+bar)=>{return foo+bar}
(4)無(wú)參數(shù)箭頭函數(shù)
如果一個(gè)箭頭函數(shù)無(wú)參數(shù)傳入兔甘,需要用一對(duì)空的括號(hào)來(lái)表示空的參數(shù)列表。
//Syntax:()=>statement
const greet=()=>'hello world'
模板字符串
當(dāng)我們使用普通的字符串時(shí)鳞滨,會(huì)使用單引號(hào)或雙引號(hào)來(lái)包裹字符串的內(nèi)容洞焙,在ES2015的模板字符串中使用反勾號(hào)`。
//Syntax:`string...`
const str=`something`
(1)支持元素注入:
可以將一些元素注入到ES2015的模板字符串中。
//Syntax:`before-${injectVariable}-after`
const str="hello world"
const num=1
const bool=true
const obj={foo:'bar'}
const arr=[1,2,3]
const str1=`String:${str}` //=>String:hello world
const str2=`Number:${num}` //=>Number:1
const str3=`Boolean:${bool}` //=>Boolean:true
const str4=`Object:${obj}` //=>Object:[object Object]
const str5=`Array:${arr}` //=>Array:1,2,3
(2)支持換行:
/**
*Syntax:`
*content
*`
*/
const sql=`
select * from Users
where FirstName='mike'
limit 5;
`
多行字符串無(wú)法像普通字符串使用雙引號(hào)嵌套單引號(hào)來(lái)表達(dá)字符串中的字符串澡匪,可以使用反斜杠將需要顯示的反勾號(hào)轉(zhuǎn)義為普通的字符熔任。添加了\`用于打印`。
const str1="Here is the outer string.'This is a string in another string'"
const str2=`Here is the outer string.\`This is a string in another string\``
對(duì)象字面量擴(kuò)展語(yǔ)法
在ES2015之前的ECMAScript的標(biāo)準(zhǔn)中唁情,對(duì)象字面量只是一種用于表達(dá)對(duì)象的語(yǔ)法疑苔,只具有表達(dá)的功能,并不起到更大的作用甸鸟。在ES2015中惦费,為ECMASCript開(kāi)發(fā)者開(kāi)放了更多關(guān)于對(duì)象的操作權(quán)限,其中便有更多的對(duì)象字面量語(yǔ)法抢韭。
(1)函數(shù)類(lèi)屬性的省略語(yǔ)法:
ES2015中引入了類(lèi)機(jī)制(Class)薪贫,普通的對(duì)象字面量也吸收了一些語(yǔ)法糖,可以讓方法屬性省略function刻恭,以一種直觀的語(yǔ)法來(lái)表達(dá)瞧省。
//Syntax:{method(){...}}
const obj={
//before
foo:function(){
return 'foo'
},
//after
bar(){
return 'bar'
}
}
有了這個(gè)語(yǔ)法糖,對(duì)象字面量中的方法類(lèi)屬性更像是一個(gè)方法鳍贾,而不只是一個(gè)以函數(shù)為值得屬性鞍匾。
(2)支持_proto_注入:
在ES2015中開(kāi)放了向?qū)ο笞置媪孔⑷隷proto_的功能,這樣做的意義在于開(kāi)發(fā)者可以得到更高的操作權(quán)限贾漏,從而更加靈活地創(chuàng)建和操作對(duì)象候学。
在ES2015標(biāo)準(zhǔn)中藕筋,開(kāi)發(fā)者允許直接向一個(gè)對(duì)象字面量注入_proto_纵散,使其直接成為指定類(lèi)的一個(gè)實(shí)例,無(wú)須另外創(chuàng)建一個(gè)類(lèi)來(lái)實(shí)現(xiàn)繼承隐圾。
//Syntax:{_proto_:...}
import {EventEmitter} from 'events'
const machine={
_proto_:new EventEmitter(),
method(){...}
}
console.log(machine) //=>EventEmitter{}
console.log(machine instanceof EventEmitter) //=>true
(3)可動(dòng)態(tài)計(jì)算的屬性名:
在ES2015標(biāo)準(zhǔn)對(duì)于對(duì)象字面量的處理中伍掀,引入了一個(gè)新語(yǔ)法,這個(gè)語(yǔ)法允許我們直接使用一個(gè)表達(dá)式來(lái)表達(dá)一個(gè)屬性名暇藏。
//Syntax:{[statement]:value}
const prefix='es2015'
const obj={
[prefix+'enhancedObject']:'foobar'
}
(4)將屬性名定義省略:
在某些場(chǎng)景中蜜笤,需要將一些已經(jīng)別定義的變量(或常量)作為其它對(duì)象字面量的屬性值進(jìn)行返回或傳入操作。
//Syntax:{injectVariable}
const foo=123
const bar =()=>foo
const obj={
foo,
bar
}
console.log(obj) //=>{foo:123,bar:[Function:bar]}
表達(dá)式結(jié)構(gòu)
在ES2015之前工程師們一般使用對(duì)象字面量和數(shù)組來(lái)模擬函數(shù)多返回值盐碱,在ES2015中同樣可以使用類(lèi)似的語(yǔ)法來(lái)實(shí)現(xiàn)函數(shù)多返回值把兔,且語(yǔ)法上更加簡(jiǎn)潔。
(1)使用對(duì)象作為返回載體(帶有標(biāo)簽的多返回值)
//Syntax:{arg1,arg2}={arg1:value1,arg2:value2}
function getState(){
return {
error:null,
logined:true,
user:{},
}
}
const {error,logined,user}=getState()
(2)使用數(shù)組作為返回載體
使用數(shù)組作為返回載體與使用對(duì)象作為返回載體的區(qū)別是:數(shù)組需要讓被賦予的變量(或常量)名按照數(shù)組的順序獲得值瓮顽。
//Syntax:[arg1,arg2]=[value1,value2]
const[foo,bar]=[1,2]
console.log(foo,bar) //=>1 2
跳過(guò)數(shù)組中某些元素,通過(guò)空開(kāi)一個(gè)元素的方式來(lái)實(shí)現(xiàn)。
//Syntax:[arg1, ,bar]=[1,2,3]
console.log(foo,bar) //=>1 3
不定項(xiàng)的獲取后續(xù)元素熬芜,用...語(yǔ)句實(shí)現(xiàn)参淫。
//Syntax:[arg1,arg2,...restArgs]=[value1,value2,value3,value4]
const [a,b,...rest]=[1,2,3,4,5]
console.log(a,b) //=>1 2
console.log(rest) //=>[3,4,5]
(3)使用場(chǎng)景
- Promise與模式匹配
注意:如果在Promise.then方法中傳入的是一個(gè)帶有解構(gòu)參數(shù)的箭頭函數(shù)時(shí),解構(gòu)參數(shù)外必須要有一個(gè)括號(hào)包裹,否則會(huì)拋出語(yǔ)法錯(cuò)誤晾咪。
function fetchData(){
return new Promise((resolve,reject)=>{
resolve(['foo','bar'])
})
}
fetchData().then(([value1,value2])=>{
console.log(value1,value2) //=>foo bar
})
fetchData().then([value1,value2]=>{ //=>SyntaxError
//...
})
如果參數(shù)過(guò)多但在某些場(chǎng)景下并不需要全部參數(shù)收擦,或者文檔約定不完善的情況下,使用對(duì)象作為傳遞載體更佳谍倦。
function fetchData(){
return new Promise((resolve,reject)=>{
resolve({
code:200,
message:ok,
data:['foo','bar']
})
})
}
fetchData().then(({data})=>{
console.log(data) //=>foo bar ...
})
- Swap(變量值交換)
Swap表示定義一個(gè)函數(shù)或一種語(yǔ)法來(lái)交換兩個(gè)變量的值塞赂。在ES2015中,可以使用模式匹配來(lái)實(shí)現(xiàn)Swap剂跟。
function swap(a,b){
var tmp=a
a=b
b=tmp
}
let foo=1
let bar=2
//Before Swap
console.log(foo,bar) //=>1 2
//Swap
[foo,bar]=[bar,foo]
//After Swap
console.log(foo,bar) //=>2 1
(4)高級(jí)用法
- 解構(gòu)別名
如果不想使用其中的屬性名作為新的變量名(或常量名)减途,可以使用別名獲得相應(yīng)的返回值,只要在原來(lái)的返回值后面加上“:x”曹洽,其中x就是希望使用的變量名鳍置。
function fetchData(){
return{
response:['foo','bar']
}
}
const{response:data}=fetchData()
console.log(data) //=>foo bar
- 無(wú)法匹配的缺省值
如果在模式匹配中送淆,存在無(wú)法匹配的缺省值(載體對(duì)象不存在相應(yīng)的值或目標(biāo)參數(shù)所對(duì)應(yīng)下標(biāo)超出了載體數(shù)組的下標(biāo)范圍),默認(rèn)情況下會(huì)返回undefined。
//Object
const {foo,bar}={foo:1}
console.log(foo,bar) //=>1 undefined
//Array
const [a,b,c]=[1,2]
console.log(a,b,c) //=>1 2 undefined
如果不希望得到undefined,可以為參數(shù)賦予一個(gè)默認(rèn)值,當(dāng)無(wú)法匹配到相應(yīng)的值時(shí),會(huì)使用該默認(rèn)值。
const {foo=1}={bar:1}
console.log(foo) //=>1
const [a,b=2]=[1]
console.log(a,b) //=>1 2
- 深層匹配
通過(guò)嵌套解構(gòu)表達(dá)式來(lái)獲取深層的內(nèi)容,可以在對(duì)象中嵌套數(shù)組來(lái)獲取對(duì)象中數(shù)組的某元素祟绊,反之亦然嘉熊。
//Object in Object
const {a,b:{c}}={a:1,b:{c:2}}
console.log(a,c) //=>1 2
//Array in Object
const {d,e:[f]}={d:1,e:[2,3]}
console.log(d,f) //=>1 2
//Object in Array
consot [g,{h}]=[1,{h:2}]
console.log(g,h) //=>1 2
//Array in Array
const [i,[j]]=[1,[2,3]]
console.log(i,j) //=>1 2
函數(shù)參數(shù)表達(dá)、傳參
(1)默認(rèn)參數(shù)值
使用語(yǔ)法:
ES2015中使用語(yǔ)法直接實(shí)現(xiàn)默認(rèn)參數(shù)值語(yǔ)法顯得更加簡(jiǎn)潔而直觀晨炕。
//Syntax:function name(arg=defaultValue){...}
function fn(arg='foo'){
console.log(arg)
}
fn() //=>foo
fn('bar') //=>bar
使用場(chǎng)景:
同時(shí)提供回調(diào)函數(shù)和Promise返回方式的接口
const noop=()=>{}
function api(callback=noop){
return new Promise((resolve,reject)=>{
const value='footbar'
resolve(value)
callback(null,value)
})
}
//Callback
api((err,data)=>{
if(err) return console.error(err)
})
//Promise
api().then(value=>{
//...
})
.catch(err=>console.error(err))
函數(shù)的默認(rèn)參數(shù)特性用在某一個(gè)對(duì)象的方法中费奸,所指定的默認(rèn)參數(shù)還可以被定為該對(duì)象的某一個(gè)屬性
const obj={
msg:'World',
greet(message=this.msg){
console.log(`Hello ${message}`)
}
}
obj.greet() //=>Hello World
obj.greet('ES2015') //=>Hello ES2015
(2)剩余參數(shù)
使用語(yǔ)法
ES2015中對(duì)剩余參數(shù)有了更為優(yōu)雅和標(biāo)準(zhǔn)的語(yǔ)法,直接將需要獲取的參數(shù)列表轉(zhuǎn)換為一個(gè)正常數(shù)組,以便使用戈二。
//Syntax:function fn([arg,]...restArgs){}
function fn(foo, ...rest){
console.log(`foo:${foo}`)
console.log(`Rest Arguments:${rest.join(',')}`)
}
fn(1,2,3,4,5)
//=>foo:1
//Rest Arguments:2,3,4,5
使用場(chǎng)景
十分常用的merge和mixin函數(shù)(合并對(duì)象)就會(huì)需要使用到剩余函數(shù)這個(gè)特性來(lái)實(shí)線觉吭。
function merge(target={},...objs){
for(const obj of objs){
const keys=Object.keys(obj)
for(const key of keys){
target[key]=obj(key)
}
}
return target
}
console.log(merge({a:1},{b:2},{c:3})) //=>{a:1,b:2,c:3}
注意事項(xiàng)
注意:一旦一個(gè)函數(shù)的參數(shù)列表中使用了剩余參數(shù)的語(yǔ)法糖仆邓,便不可以再添加任何參數(shù)鲜滩,否則會(huì)拋出錯(cuò)誤节值。
function fn1(...rest){} //Correct
function fn1(...rest,foo){} //Syntax Error
arguments與剩余函數(shù)
雖然從語(yǔ)言角度看,arguments和...args是可以同時(shí)使用的豌汇,但有一種情況除外,arguments在箭頭函數(shù)中佛嬉,會(huì)跟隨上下文綁定到上層逻澳,所以在不確定上下文綁定結(jié)果的情況下,盡可能不要在箭頭函數(shù)中使用arguments暖呕,而要使用..args斜做。
(3)解構(gòu)傳參
ES2015中的解構(gòu)傳參是使用數(shù)組作為傳入?yún)?shù)以控制函數(shù)的調(diào)用情況,不同的是解構(gòu)傳參不會(huì)替換函數(shù)調(diào)用中的上下文湾揽。
與剩余參數(shù)一樣陨享,解構(gòu)傳參使用...作為語(yǔ)法糖標(biāo)識(shí)符。
//Syntax:fn(...[arg1,arg2])
function sum(...numbers){
return numbers.reduce((a,b)=>a+b)
}
sum(...[1,2,3]) //=>6
新的數(shù)據(jù)解構(gòu)
在ECMAScript中定義了以下幾種基本的數(shù)據(jù)結(jié)構(gòu)钝腺,分為值類(lèi)型(Primitive Types)和引用類(lèi)型(Reference
Types)抛姑。
值類(lèi)型數(shù)據(jù)結(jié)構(gòu):
- String 字符串
- Number 數(shù)值
- Boolean 布爾型(true與false)
- Null 空值
- Undefined 未定義值
引用類(lèi)型數(shù)據(jù)結(jié)構(gòu):
- Object 對(duì)象
- Array 數(shù)組
- RegExp(Regular Expression with pattern)正則表達(dá)式
- Date 日期
- Error 錯(cuò)誤
(1)Set有序集合
ECMAScript中,Array表示一系列元素的有序集合艳狐,其中每一個(gè)元素都會(huì)帶有自身處在這個(gè)集合內(nèi)的位置并以自然數(shù)作為標(biāo)記定硝,即帶有下標(biāo)。無(wú)序集合可以把它當(dāng)成沒(méi)有排序概念的數(shù)組毫目,并且元素不可重復(fù)蔬啡。
使用語(yǔ)法
在ES2015中,集合與數(shù)組不一樣的是镀虐,集合無(wú)法像數(shù)組那樣使用[]語(yǔ)法來(lái)直接生成箱蟆,而需要用新建對(duì)象的方法來(lái)創(chuàng)建一個(gè)新的集合對(duì)象。
//Syntax:new Set([iterable]):Set
const set=new Set()
可以使用一個(gè)現(xiàn)成的數(shù)組作為集合對(duì)象的初始元素
const set=new Set([1,2,3])
集合對(duì)象的操作方法
增刪元素
可以通過(guò)add刮便、delete和clear方法來(lái)添加空猜,刪除,清空集合內(nèi)的元素恨旱。
const set =new Set()
//添加元素
set.add(1)
.add(2)
.add(3)
.add(3) //這一句不會(huì)起到任何作用辈毯,因?yàn)樵?已存在于集合內(nèi)
console.log(set) //Set{1,2,3}
//刪除元素
set.delete(2)
console.log(set) //Set{1,3}
//清空集合
set.clear()
console.log(set) //set{}
檢查元素
const set=new Set([1,2,3])
//檢查元素
set.has(2) //=>true
set.has(4) //=>false
遍歷元素
集合對(duì)象自身定義了forEach方法,跟數(shù)組類(lèi)型中的forEach一樣搜贤,傳入一個(gè)回調(diào)函數(shù)以接受集合內(nèi)的元素谆沃,并且可以為這個(gè)回調(diào)函數(shù)指定一個(gè)上下文。
const set=new Set([1,2,3,4])
set.forEach(item=>{
console.log(item)
})
//=>1 2 3 4
set.forEach(item=>{
console.log(item*this.foo)
},{foo:2})
//=>2 4 6 8
在ES2015中仪芒,由于Symbol的引入唁影,數(shù)組等類(lèi)型有了一個(gè)新屬性Symbol.iterator(迭代子)耕陷,這些類(lèi)型的新名稱(chēng)--可迭代對(duì)象(Iterable Object),其中包括數(shù)組類(lèi)型据沈、字符串類(lèi)型、集合類(lèi)型卓舵、字典類(lèi)型(Map)南用、生成器類(lèi)型(Generator),for-of循環(huán)語(yǔ)句可以對(duì)可迭代對(duì)象進(jìn)行迭代掏湾,配合const或let使用裹虫,從而解決forEach方法不可中斷的問(wèn)題。
const set=new Set([1,2,3,4])
for(const val of set){
console.log(val)
}
//=>1 2 3 4
(2)WeakSet
WeakSet最大的應(yīng)用意義在于融击,可以直接對(duì)引擎中垃圾收集器的運(yùn)行情況有程序化的探知方式筑公,開(kāi)發(fā)者可以利用WeakSet的特性以更高的定制化方案來(lái)優(yōu)化程序的內(nèi)存使用方案。
WeakSet與Set的區(qū)別:
a.WeakSet不能包含值類(lèi)型元素尊浪,否則會(huì)拋出一個(gè)TypeError匣屡;
b.WeakSet不能包含無(wú)引用的對(duì)象,否則會(huì)自動(dòng)清除出集合拇涤;
c.WeakSet無(wú)法被探知其大小捣作,也無(wú)法被探知其中所包含的元素。
(3)Map映射類(lèi)型
映射類(lèi)型在計(jì)算機(jī)科學(xué)中的定義屬于關(guān)聯(lián)數(shù)組(Associative Array)鹅士,關(guān)聯(lián)數(shù)組的定義為若干個(gè)鍵值對(duì)(Key/Value Pair)組成的集合券躁,其中每一個(gè)鍵都只能出現(xiàn)一次。
使用語(yǔ)法
映射類(lèi)型需要?jiǎng)?chuàng)建一個(gè)相應(yīng)的實(shí)例來(lái)使用掉盅。
//Syntax:new Map([iterable]):Map
const map=new Map()
在創(chuàng)建映射對(duì)象時(shí)也拜,可以將一個(gè)以二元數(shù)組(鍵值對(duì))作為元素的數(shù)組傳入到構(gòu)建函數(shù)中,其中每一個(gè)鍵值對(duì)都會(huì)加入到該映射對(duì)象中。該數(shù)組內(nèi)的元素會(huì)以數(shù)組順序進(jìn)行處理趾痘,如果存在相同的鍵慢哈,則會(huì)按照FIFO(First In First Out,先進(jìn)先出)原則永票,以該鍵最后一個(gè)處理的對(duì)應(yīng)值為最終值卵贱。
const map = new Map([['foo', 1 ], [ 'foo', 2 ]])
console.log(map.get('foo')) //=> 2
與對(duì)象字面量一樣,映射對(duì)象可以對(duì)其中的鍵值對(duì)進(jìn)行添加瓦侮、檢查艰赞、獲取佣谐、刪除等操作肚吏。
當(dāng)然,作為新特性的映射對(duì)象也擁有一些Object沒(méi)有的方法狭魂。
增刪鍵值對(duì)
與集合對(duì)象類(lèi)似罚攀,可以通過(guò)set党觅、delete和clear方法對(duì)映射對(duì)象內(nèi)的鍵值對(duì)進(jìn)行操作。
const map=new Map()
// 添加鍵值對(duì)
map.set('foo','hello')
map.set('bar','es2015')
map.set('bar','world') //=>將覆蓋之前加入的值
//刪除指定的鍵值對(duì)
map.delete('foo')
//清空映射對(duì)象
map.clear()
獲取鍵值對(duì)
映射對(duì)象由鍵值對(duì)組成斋泄,所以可以利用鍵來(lái)獲取相應(yīng)的值杯瞻。
const map=new Map()
map.set('foo','bar')
console.log(map.get('foo')) //=> bar
檢查鍵值對(duì)
映射對(duì)象可以通過(guò)has (key)方法來(lái)檢査其中是否包含某一個(gè)鍵值對(duì)
const map=new Map([ 'foo', 1 ])
console.log(map.has('foo')) //=> true
console.log(map.has('bar')) //=>false
遍歷鍵值對(duì)
映射對(duì)象是關(guān)聯(lián)數(shù)組的一種實(shí)現(xiàn),所以映射對(duì)象在設(shè)計(jì)上同樣是一種可迭代對(duì)象炫掐,可以通過(guò)for-of循環(huán)語(yǔ)句對(duì)其中的鍵值對(duì)進(jìn)行歷遍魁莉。也可以使用己實(shí)現(xiàn)在映射對(duì)象中的forEach方法來(lái)進(jìn)行歷遍。
映射對(duì)象帶有entries ()方法募胃,這個(gè)與集合對(duì)象中的entries()類(lèi)似旗唁,用于返回一個(gè)包
含所有鍵值對(duì)的可迭代對(duì)象,而for-of循環(huán)語(yǔ)句和forEach便是先利用entries ()方法先
將映射對(duì)象轉(zhuǎn)換為一個(gè)類(lèi)數(shù)組對(duì)象痹束,然后再進(jìn)行迭代检疫。
const map=new Map([['foo',1],['bar',2]])
console.log(Array.from(map.entries())) //=>[['bar',1],['bar',2]]
for(const [key,value] of map){
console.log(`${key}:${value}`)
}
//=>foo:1 bar:2
map.forEach((value,key,map)=>{
console.log(`${key}:${value}`)
})
(4)WeakMap
WeakMap的鍵會(huì)檢查變量引用,只要其中任意一個(gè)引用被解除祷嘶,該值對(duì)就會(huì)被刪除屎媳。
//Syntax:new WeakMap([iterable]):WeakMap
const weakm=new WeakMap()
let keyObject={id:1}
const valObject={score:100}
weakm.set(keyObject,valObject)
weakm.get(keyObject) //=>{score:100}
keyObject=null
console.log(weakm.has(keyObject)) //=>false
類(lèi)語(yǔ)法
ES2015中的類(lèi)語(yǔ)法與其他C語(yǔ)言家族成員的類(lèi)語(yǔ)法有許多相同之處,如果開(kāi)發(fā)者有在
JavaScript中使用過(guò)基于原型的類(lèi)機(jī)制论巍,那么也可以很容易接受ES2015的語(yǔ)法烛谊。
基本定義語(yǔ)法
// Syntax: class name { ... }
class Animal {
constructor(family, specie, hue) {
this.family =family
this.specie = specie
this.hue = hue
yell() {
console.log(this.hue)
}
}
const doge = new Animal('Canidae', 'Canis lupus’, 'Woug')
doge.yell() //=> Woug
這里需要注意的是嘉汰,在類(lèi)中定義的方法晒来,都是帶有作用域的普通函數(shù),而不是箭頭函數(shù)郑现,方法內(nèi)第一層所引用的this都指向當(dāng)前實(shí)例湃崩,如果實(shí)例方法內(nèi)包含箭頭函數(shù),則引擎就會(huì)根據(jù)包含層級(jí)把箭頭函數(shù)內(nèi)引用的this所指向的實(shí)際對(duì)象一直向上層搜索接箫,直到到達(dá)一個(gè)函數(shù)作用域或塊級(jí)作用域?yàn)橹乖芏痢H绻恢彼阉鞯竭_(dá)了運(yùn)行環(huán)境的最上層,就會(huì)被指向undefined辛友。
class Point{
constructor(x,y){
this.x=x
this.y=y
}
moveRight(step){
return new Promise(resolve=>resolve({
x:this.x+step,
y:this.y
}))
}
}
const p=new Point(2,5)
p.moveRight(3)
.thien(({x,y})=>console.log(`(${x},${y}`)) //=>(5,5)
繼承語(yǔ)法
//Syntax:class SubClass extends SuperClass{}
class Point2D{
constructor(x,y){
this.x=x
this.y=y
}
toString(){
return `(${this.x},${this.y})`
}
}
class Point3D extends Point2D{
constructor(x,y,z){
super(x,y)
this.x=x
}
toString(){
return `(${this.x},${this.y},${this.z})`
}
}
ES2015的繼承語(yǔ)法可以將以前使用構(gòu)建函數(shù)模擬的類(lèi)作為父類(lèi)來(lái)繼承薄扁,并非只由class語(yǔ)法定義的類(lèi)才可以使用。
function Cat() {}
Cat.prototype.climb = function () {
return "I can climb"
}
Cat.prototype.yell = function () {
return "Meow"
}
class Tiger extends Cat{
yell(){
return "Aoh"
}
}
const tiger=new Tiger()
console.log(tiger.yell()) //=>Aoh
console.log(tiger.climb()) //=>I can climb
需要注意的是废累,如果一個(gè)子類(lèi)繼承了父類(lèi)邓梅,那么在子類(lèi)的constructor構(gòu)造函數(shù)中必須使用super函數(shù)調(diào)用父類(lèi)的構(gòu)造函數(shù)后才能在子類(lèi)的constructor構(gòu)造函數(shù)中使用this,否則會(huì)報(bào)出this is defined的錯(cuò)誤邑滨。
class Foo{}
class Bar extends Foo{
constructor(){
this.property=1
}
}
new Bar() //=>RerenceError:this is defined
這個(gè)問(wèn)題在除constructor構(gòu)造函數(shù)以外的方法中并不會(huì)出現(xiàn)日缨,即便在子類(lèi)的構(gòu)造
函數(shù)中并沒(méi)有調(diào)用super函數(shù),在其他方法中依然可以調(diào)用this來(lái)指向當(dāng)前實(shí)例掖看。
Getter/Setter
Getter/Setter是一種元編程(Meta-programming)的概念匣距,元編程的特點(diǎn)在于面哥,允許程序可以對(duì)運(yùn)行時(shí)(Runtime)的對(duì)象進(jìn)行讀取和操作,從而使程序可以脫離代碼從字面上為程序定義的一些限制毅待,有了對(duì)對(duì)象的更高操作權(quán)限尚卫。
const List={
_array:[],
set new(value){
this._array.push(value)
},
get last(){
return this._array[0]
},
get value(){
return this._array
}
}
List.new=1
List.new=2
List.new=3
console.log(List.last) //=>1
console.log(List.value) //=>[1,2,3]
ES2015的類(lèi)機(jī)制同樣支持Getter/Setter在類(lèi)中的使用,配合元編程的概念尸红,類(lèi)的能力會(huì)變得更加強(qiáng)大吱涉。
class Point{
constructor(x,y){
this.x=x
this.y=y
}
get d(){
return Math.sqrt(Math.pow(this.x,2)+Math.pow(this.y,2))
}
}
const p=new Point(3,4)
console.log(p.d) //=>5
靜態(tài)方法
可以通過(guò)實(shí)現(xiàn)一個(gè)靜態(tài)方法來(lái)擴(kuò)展類(lèi)
// Syntax: class Name { static fn() { ... } }
class Animal {
constructor(family, specie, hue) {
this.family = family
this.specie = specie
this.hue = hue
}
yell() {
console.log(this.hue)
}
static extend(constructor, ..._args) {
return class extends Animal {
constructor{...args) {
super(..._args)
constructor.call(this, ...args)
}
}
}
}
const Dog = Animal.extend(function(name) {
this.name = name
}, 'Canidae', 'Canis lupus', 'Woug')
const doge=new Dog('Doge')
doge.yell(> //=> Woug
console.log(doge.name) //=> Doge
高級(jí)技巧
在Object類(lèi)及其所有子類(lèi)(在ECMAScript中,除了null外里、undefined以外邑飒,一切類(lèi)型和類(lèi)都可以看做是Object的子類(lèi))的實(shí)例中,有一個(gè)利用Symbol.toStringTag作為鍵的屬性级乐,定義著當(dāng)這個(gè)對(duì)象的toString()方法被調(diào)用時(shí)疙咸,所返回的Tag的內(nèi)容是什么。這就意味著可以進(jìn)行一些自定義操作风科,通過(guò)[]語(yǔ)法和Getter特性為一個(gè)類(lèi)自定義toString標(biāo)簽撒轮。
class Foo{
get [Symbol.toStringTag](){
return 'Bar'
}
}
const obj=new Foo()
console.log(obj.toString()) //=>[object Bar]
注意事項(xiàng)
類(lèi)的繼承必須是單項(xiàng)的,不可能出現(xiàn)A類(lèi)繼承于B類(lèi)的同時(shí)B類(lèi)也繼承A類(lèi)的現(xiàn)象贼穆,這就意味著题山,父類(lèi)必須在子類(lèi)定義之前被定義。
生成器(Generator)
生成器的主要功能是:通過(guò)一段程序故痊,持續(xù)迭代或枚舉出符合某個(gè)公式或算法的有序數(shù)列中的元素顶瞳,這個(gè)程序便是用于實(shí)現(xiàn)這個(gè)公式或算法的,而不需要將目標(biāo)數(shù)列完整寫(xiě)出愕秫。
生成器是ES2015中同時(shí)包含語(yǔ)法和底層支持的一個(gè)新特性慨菱。
(1)基本概念
生成器函數(shù)
生成器函數(shù)是ES2015中生成器的最主要表現(xiàn)方式,它與普通函數(shù)的語(yǔ)法差別在于戴甩,在function語(yǔ)句之后和函數(shù)名之前符喝,有一個(gè)“*”作為生成器函數(shù)的標(biāo)示符。
function* fibo(){
//...
}
生成器函數(shù)并不是強(qiáng)制性使用聲明式進(jìn)行定義的甜孤,與普通函數(shù)—樣也可以使用表達(dá)式進(jìn)行定義协饲。
const fnName = function*() {/*...*/}
生成器函數(shù)的函數(shù)體內(nèi)容將會(huì)是所生成的生成器的執(zhí)行內(nèi)容,在這些內(nèi)容之中缴川,yield語(yǔ)句的引入使得生成器函數(shù)與普通函數(shù)有了區(qū)別茉稠。yield語(yǔ)句的作用與return語(yǔ)句冇些相似,但并非退出函數(shù)體把夸,而是切出當(dāng)前函數(shù)的運(yùn)行時(shí)(此處為一個(gè)類(lèi)協(xié)程而线,Semi-coroutine),與此同時(shí)可以將一個(gè)值(可以是任何類(lèi)型)帶到主線程中。
我們以一個(gè)比較形象的例子來(lái)做比喻,你可以把整個(gè)生成器運(yùn)行時(shí)看成一條長(zhǎng)長(zhǎng)的瑞士卷吞获,while (true)是無(wú)限長(zhǎng)的况凉,ECMAScript引擎每一次遇到y(tǒng)ield語(yǔ)句時(shí)谚鄙,就好比在瑞士卷上切一刀各拷,而切面所呈現(xiàn)的“紋路”則是yield語(yǔ)句所得的值。
生成器
從計(jì)算機(jī)科學(xué)角度上看闷营,生成器是—種類(lèi)協(xié)程或半?yún)f(xié)程(Semi-coroutine),它提供了一種可以通過(guò)特定語(yǔ)句或方法使其執(zhí)行對(duì)象(Execution)暫停的功能烤黍,而這語(yǔ)句一般都是yield語(yǔ)句。上面的斐波那契數(shù)列生成器便是通過(guò)yield語(yǔ)句將每一次的公式計(jì)算結(jié)果切出執(zhí)行對(duì)象傻盟,并帶到主線程上來(lái)的速蕊。
在ES2015中,yield語(yǔ)句可以將一個(gè)值帶出協(xié)程娘赴,向主線程也可以通過(guò)生成器對(duì)象的方法將一個(gè)值帶回生成器的執(zhí)行對(duì)象中去规哲。
const inputValue =yield outputValue
生成器切出執(zhí)行對(duì)象并帶出outputValue,主線程經(jīng)過(guò)同步或異步處理后,通過(guò).next (val)方法將inputValue帶回生成器的執(zhí)行對(duì)象中诽表。
(2)使用方法
構(gòu)建生成器函數(shù)
使用生成器的第一步自然是要構(gòu)建一個(gè)生成器函數(shù)唉锌,以生成相對(duì)應(yīng)的生成器對(duì)象。
啟動(dòng)生成器
生成器函數(shù)不能直接作為普通的函數(shù)來(lái)使用竿奏,因?yàn)樵谡{(diào)用時(shí)無(wú)法直接執(zhí)行其中的邏輯代碼袄简。執(zhí)行生成器函數(shù)會(huì)返回一個(gè)生成器對(duì)象,用于運(yùn)行生成器內(nèi)容和接受其中的值泛啸。
運(yùn)行生成器內(nèi)容
因?yàn)樯善鲗?duì)象自身也是一種可迭代對(duì)象绿语,所以直接使用for-of循環(huán)將其中輸出的值打印出來(lái)。
Promise
Promise意在讓異步代碼變得干凈和直觀候址,讓異步代碼變得井然有序吕粹。
Promise在設(shè)計(jì)上具有原子性,即只有三種狀態(tài):等待(Pending)岗仑、成功(Fulfilled)昂芜、失敗(Rejected)赔蒲。在調(diào)用支持Promise的異步方法時(shí)泌神,邏輯變得非常簡(jiǎn)單,在大規(guī)模的軟件工程開(kāi)發(fā)中具有良好的健壯性舞虱。
(1)基本語(yǔ)法
創(chuàng)建Promise對(duì)象:
要想給一個(gè)函數(shù)賦予Promise能力欢际,就要先創(chuàng)建一個(gè)Promise對(duì)象,并將其作為函數(shù)值返回矾兜。Promise對(duì)象要求傳入一個(gè)函數(shù)损趋,并帶有resolve和reject參數(shù)。這是兩個(gè)用于結(jié)束Promise等待的函數(shù)椅寺,對(duì)應(yīng)的狀態(tài)分別是成功和失敗浑槽。
//Syntax:
//new Promise(executor):Promise
//new Promise((resolve,reject)=>statements):Promise
function asyncMethod(...args){
return new Promise((resolve,reject)=>{
//...
})
}
將新創(chuàng)建的Promise對(duì)象作為異步方法的返回值蒋失,所有的狀態(tài)就可以使用它所提供的方法進(jìn)行控制了。
進(jìn)行異步操作:
創(chuàng)建了 Promise對(duì)象后桐玻,就可以進(jìn)行異步操作篙挽,并通過(guò)resolve (value)和
reject (reason)方法來(lái)控制Promise的原子狀態(tài)。
- resolve(value)方法控制的是當(dāng)前Promise對(duì)象是否進(jìn)入成功狀態(tài)镊靴,一旦執(zhí)行該方法并傳入有且只有一個(gè)返回值铣卡,Promise便會(huì)從等待狀態(tài)(Pending)進(jìn)入成功狀態(tài)(Fulfilled),Promise也不會(huì)再接收任何狀態(tài)的變偏竟。
- reject (reason)方法控制的是當(dāng)前Promise對(duì)象是否進(jìn)入失敗階段煮落,與resolve方法相冋,一旦進(jìn)入失敗階段就無(wú)法再改變踊谋。
//Syntax:
//resolve(value)
//reject(reason)
new Promise((resolve,reject)=>{
api.call('fetch-data',(err,data)=>{
if(err) return reject(err)
resolve(data)
})
})
其中在Promise的首層函數(shù)作用域中一旦出現(xiàn)throw語(yǔ)句蝉仇,Promise對(duì)象便會(huì)直接進(jìn)入失敗狀態(tài),并以throw語(yǔ)句的拋出值作為錯(cuò)誤值進(jìn)行錯(cuò)誤處理殖蚕。
(new Promise(function() {
throw new Error ('test')
)))
.catch(err =>console.error(err))
但是相對(duì)的return語(yǔ)句并不會(huì)使Promise對(duì)象進(jìn)入成功狀態(tài)轿衔,而會(huì)使Promise停留在等待狀態(tài)。所以在Promise對(duì)象的執(zhí)行器(executor)內(nèi)需要謹(jǐn)慎使用return語(yǔ)句來(lái)控制代碼流程嫌褪。
處理Promise的狀態(tài)
與resolve(value)和reject(reason)方法對(duì)應(yīng)的是呀枢,Promise對(duì)象有兩個(gè)用于處理Promise對(duì)象狀態(tài)變化的方法。
這兩個(gè)方法都會(huì)返回一個(gè)Promise對(duì)象笼痛,Promise對(duì)象的組合便會(huì)成為一個(gè)Promise對(duì)象鏈裙秋,呈流水線的模式作業(yè)。
//Syntax:promise.then(onFulfilled).catch(onRejected):Promise
asyncMethod()
.then((...args)=>args /*...*/)
.catch(err=>console.error(err))
Promise鏈?zhǔn)教幚砟J(rèn)被實(shí)現(xiàn)缨伊,即.then(onFulfilled)或.catch(onRejected)會(huì)處理在onFulfilled和onRejected中所返回或拋出的值摘刑。
如果onFulfilled或onRejected中所返回的值是一個(gè)Promise對(duì)象,則該P(yáng)romise對(duì)象會(huì)被加入到Promise的處理鏈中刻坊。
如果onFulfilled或onRejected中返回的值并不是一個(gè)Promise對(duì)象枷恕,則會(huì)返回一個(gè)己經(jīng)進(jìn)入成功狀態(tài)的Promise對(duì)象。
如果onFulfilled或onRejected中因?yàn)閠hrow語(yǔ)句而拋出一個(gè)錯(cuò)誤err谭胚,則會(huì)返回一個(gè)已經(jīng)進(jìn)入失敗狀態(tài)的Promise對(duì)象徐块。
之所以說(shuō)Promise對(duì)象鏈呈流水線的模式進(jìn)行作業(yè),是因?yàn)樵赑romise對(duì)象對(duì)自身的onFulfilled和onRejected響應(yīng)器的處理中灾而,會(huì)對(duì)其中返回的Promise對(duì)象進(jìn)行處理胡控。其內(nèi)部會(huì)將這個(gè)新的Promise對(duì)象加入到Promise對(duì)象鏈中,并將其暴露出來(lái)旁趟,使其繼續(xù)接受新的Promise對(duì)象的加入昼激。只有當(dāng)Promise對(duì)象鏈中的上一個(gè)Promise對(duì)象進(jìn)入成功或失畋階段,下一個(gè)Promise對(duì)象才會(huì)被激活,這就形成了流水線的作業(yè)模式橙困。
Promise對(duì)象鏈還有一個(gè)十分實(shí)用的特性--Promise對(duì)象的狀態(tài)是具有傳遞性的瞧掺。
如果Promise對(duì)象鏈中的某一環(huán)出現(xiàn)錯(cuò)誤,Premise對(duì)象鏈便會(huì)從出錯(cuò)的環(huán)節(jié)開(kāi)始凡傅,不斷向下傳遞辟狈,直到出現(xiàn)任何一環(huán)的Promise對(duì)象對(duì)錯(cuò)誤進(jìn)行響應(yīng)為止。
(2)高級(jí)使用方法
Promise.all(iterable)
該方法可以傳入一個(gè)可迭代對(duì)象(如數(shù)組)像捶,并返回一個(gè)Promise對(duì)象上陕,該P(yáng)romise對(duì)象會(huì)
在當(dāng)可迭代對(duì)象中的所冇Promise對(duì)象都進(jìn)入完成狀態(tài)(包括成功和失畋)后被激活桩砰。
1.如果可迭代對(duì)象中的所有Promise對(duì)象都進(jìn)入了成功狀態(tài)拓春,那么該方法返回的Promise
對(duì)象也會(huì)進(jìn)入成功狀態(tài),并以一個(gè)可迭代對(duì)象來(lái)承載其中的所有返回值亚隅。
2.如果可迭代對(duì)象中Promise對(duì)象的其中一個(gè)進(jìn)入了失敗狀態(tài)硼莽,那么該方法返回的Promise
對(duì)象也會(huì)進(jìn)入失敗狀態(tài),并以那個(gè)進(jìn)入失敗狀態(tài)的錯(cuò)誤信息作為自己的錯(cuò)誤信息煮纵。
//Syntax:Promise.all(iterable):Promise
const promises=[async(1),async(2),async(3),async(4)]
Promise.all(promises)
.then(values=>{
//...
})
.catch(err=>console.error(err))
Promise.race(iterable)
Promise .race (iterable)方法同樣也接受一個(gè)包含若干個(gè)Promise對(duì)象的可迭代對(duì)象懂鸵,但不同的是這個(gè)方法會(huì)監(jiān)聽(tīng)所有的Promise對(duì)象,并等待其中的第一個(gè)進(jìn)入完成狀態(tài)的Promise對(duì)象行疏,一旦有第一個(gè)Promise對(duì)象進(jìn)入了完成狀態(tài)匆光,該方法返回的Promise對(duì)象便會(huì)根據(jù)這第一個(gè)完成的Promise對(duì)象的狀態(tài)而改變。
//Syntax:Promise.race(iterable):Promise
const promises=[async(1),async(2),async(3),async(4)]
Promise.race(promises)
.then(values=>{
//...
})
.catch(err=>console.error(err))
代碼模塊化
ECMAScript包含了以往模塊加載庫(kù)的主要功能酿联,還添加了一些非常使用的設(shè)計(jì)终息,以提高ECMAScript的模塊化管理功能。
(1)引入模塊
ES Module中有很多種引入模塊的方法贞让,最基本的便是import語(yǔ)句周崭。
import name form 'module-name'
import * as name from 'module-name'
import {member} from 'module-name'
import {meber as alias} from 'module-name'
import 'module-name'
引入默認(rèn)模塊
//Syntax:import namespace from 'module-name'
import http from 'http'
import url from 'url'
import fs from 'fs'
引入模塊部分接口
ES2015中的 模塊化機(jī)制支持引入一個(gè)模塊的部分接口
//Syntax:import {meber1,meber2} from 'module-name'
import {isEmpty} from 'lodash'
import {EventEmitter} from 'events'
console.log(isEmpty({})) //=>true
從模塊中局部引用的接口定義一個(gè)別名,以避免指代不明或接口重名的情況出現(xiàn)喳张。
//Syntax:import {meber as alias} from 'module-name'
import {createServer as createHTTPServer} from 'http'
import {createServer as createHTTPSServer} from 'https'
引入全部局部接口到指定命名空間
有的模塊不會(huì)定義默認(rèn)接口续镇,只是定義了若干個(gè)命名接口,將其中的所有接口定義到一個(gè)命名空間中销部,使用以下語(yǔ)法摸航。
//Syntax:import * as namespace from 'module-name'
import * as lib from 'module'
lib.method1()
lib.method2()
混入引入默認(rèn)接口和命名接口
同時(shí)引入默認(rèn)接口和其它命名接口,可以通過(guò)混合語(yǔ)句來(lái)實(shí)現(xiàn)舅桩。
//Syntax:import {default as <default name>,method1} from 'module-name'
import {default as Client,utils} from 'module'
注意:引入的默認(rèn)接口必須要使用as語(yǔ)句被賦予一個(gè)別名酱虎,因?yàn)樵诔K引入語(yǔ)句以外的地方default是一個(gè)保留關(guān)鍵字,所以無(wú)法使用江咳。
import {default ,utils} from 'module' //Wrong
簡(jiǎn)潔的語(yǔ)法
//Syntax:import <default name>,{<named modules>} from 'module-name'
import Client,{utils} from 'module'
import Client,* as lib from 'module'
不引入接口逢净,僅運(yùn)行模塊代碼
在某些場(chǎng)景下,一些模塊并不需要向外暴露任何接口,只需要執(zhí)行內(nèi)容的代碼(如系統(tǒng)初始化)爹土。
//Syntax:import 'module-name'
import 'system-apply'
(2)定義模塊
ES Module中以文件名及其相對(duì)或絕對(duì)路徑作為該模塊被引用時(shí)的標(biāo)識(shí)甥雕。
(3)暴露模塊
暴露單一接口
如果需要定義一個(gè)項(xiàng)目?jī)?nèi)的工具集模塊,需要將其中定義的函數(shù)或者對(duì)象暴露到該文件所定義的模塊上胀茵。
//Syntax:export <statement>
//module.js
export const apiRoot='http://example.com/api'
export function method(){
//...
}
export class foo{
//...
}
//app.js
import {method,foo} from 'module.js'
export 語(yǔ)句后所跟著的語(yǔ)句需要具有生命部分和賦值部分
1.聲明部分(Statement)為export語(yǔ)句提供了所暴露接口的標(biāo)識(shí)社露;
2.賦值部分(Assignment)為export語(yǔ)句提供了接口的值。
那些不符合這兩個(gè)條件的語(yǔ)句無(wú)法被暴露在當(dāng)前文件所定義的模塊上琼娘,以下代碼被視為非法代碼峭弟。
//1
export 'foo'
//2
const foo='bar'
export foo
//3
export function(){}
暴露模塊默認(rèn)接口
在某些時(shí)候,一個(gè)模塊只需要暴露一個(gè)接口脱拼,比如需要使用模塊機(jī)制定義一個(gè)只含有一個(gè)單一工具類(lèi)的模塊時(shí)瞒瘸,就沒(méi)有必要讓這個(gè)工具類(lèi)成為該模塊的一部分,而是讓這個(gè)類(lèi)成為這個(gè)模塊熄浓。
//Syntax:export default <value>
//client.js
export default class Client{
//...
}
//app.js
import Client from 'client.js'
混合使用暴露接口語(yǔ)句
開(kāi)發(fā)者可以為一個(gè)模塊同時(shí)定義默認(rèn)接口和其它命名接口情臭。
//module.js
export default class Client{
//...
}
export const foo='bar'
//app.js
import Client,{foo} from 'module'
暴露一個(gè)模塊的所有接口
在第三方類(lèi)庫(kù)的開(kāi)發(fā)中,不免需要將各種不同的功能塊分成若干個(gè)模塊來(lái)進(jìn)行開(kāi)發(fā)赌蔑,以便管理俯在。ES Module可以將import語(yǔ)句和export組合,直接將一個(gè)模塊的接口暴露到另外一個(gè)模塊上娃惯。
//Syntax:export * from 'other-module'
//module-1.js
export function foo(){/*....*/}
//module.js
export * from 'module-1'
//app.js
import {foo} from 'module'
暴露一個(gè)模塊的部分接口
//Syntax:export {member} from 'module-name'
export {member} from 'module'
export {default as ModuleDefault} from 'module'
暴露一個(gè)模塊的默認(rèn)接口
可以將一個(gè)模塊的默認(rèn)接口作為另一個(gè)模塊的默認(rèn)接口跷乐。
export {default} from 'module'
Symbol
Symbol的值具有互不等價(jià)的特性,開(kāi)發(fā)者同時(shí)可以為Symbol值添加一個(gè)描述趾浅。
(1)基本語(yǔ)法
- 生成唯一的Symbol值
執(zhí)行Symbol({description})函數(shù)可以生成一個(gè)與其它Symbol值互不等價(jià)的Symbol值愕提,其中Symbol()函數(shù)可以接受一個(gè)除Symbol值以外的值作為該Symbol值的描述,以便通過(guò)開(kāi)發(fā)者的辨認(rèn)判斷其為可選的潮孽。
//Syntax:Symbol([description]):Symbol
const symbol=Symbol() //=>Symbol()
const symbolForSomething=Symbol('something') //=>Symbol(something)
const symbolWithNumber=Symbol(3.14) //=>Symbol(3.14)
const symbolWidthObject=Symbol({'foo':'bar'}) //=>Symbol([object Object])
//Don't use a symbol to be another symbol's description
const anotherSymbol=Symbol(symbol) //=>TypeError:Cannot convert a Symbol value to a string
描述值僅僅是起到描述的作用揪荣,不會(huì)對(duì)Symbol值本身起到任何改變的作用。即便是兩個(gè)具有相同描述值的Symbol值也不具有等價(jià)性往史。
const symbol1=Symbol('footer')
const symbol2=Symbol('footer')
symbol1==symbol2 //=>false
注意:Symbol函數(shù)并不是一個(gè)構(gòu)造函數(shù)仗颈,不能使用new語(yǔ)句來(lái)生成Symbol“對(duì)象”,否則會(huì)拋出TypeError錯(cuò)誤椎例。
new Symbol() //=>TypeError:Symbol is not a constructor
由此可知挨决,Symbol是一種值類(lèi)型而非引用類(lèi)型。這就意味著如果將Symbol值作為函數(shù)形參進(jìn)行傳遞订歪,將會(huì)進(jìn)行復(fù)制值傳遞而非引用傳遞务热,這跟其它值類(lèi)型(字符串级及,數(shù)字等)的行為是一致的掌实。
const symbol=Symbol('hello')
function fn1(_symbol){
return _symbol==symbol
}
console.log(fn1(symbol)) //=>true
function fn2(_symbol){
_symbol=null
console.log(_symbol)
}
fn2(symbol) //=>null
如果希望得到一個(gè)Symbol“對(duì)象”矢否,可以使用Object()函數(shù)實(shí)現(xiàn)慎陵。
const symbol=Symbol('foo')
typeof symbol //=>symbol
const symbolObj=Object(symbol)
typeof symbolObj //=>object
- 注冊(cè)全局可重用Symbol
ES2015標(biāo)準(zhǔn)除了提供具有唯一性的Symbol值以外,同樣還允許開(kāi)發(fā)者在當(dāng)前運(yùn)行時(shí)中定義一些全局有效性的Symbol喻奥。開(kāi)發(fā)者可以通過(guò)一個(gè)key向當(dāng)前運(yùn)行時(shí)注冊(cè)一個(gè)需要在其他程序中使用的Symbol席纽。
//Syntax:Symbol.for([key]):Symbol
const symbol=Symbol.for('footer')
Symbol. for ()與Symbol ()的區(qū)別是,Symbol . for ()會(huì)根據(jù)傳入的key在全局作用域中注冊(cè)一個(gè)Symbol值撞蚕,如果某一個(gè)key從未被注冊(cè)到全局作用域中润梯,便會(huì)創(chuàng)建一個(gè)Symbol值并根據(jù)key注冊(cè)到全局環(huán)境中。如果該key己被注冊(cè)甥厦,就會(huì)返冋一個(gè)與第一次使用所創(chuàng)建的Symbol值等價(jià)的Symbol值纺铭。
const symbol = Symbol.for('foo')
const obj ={}
obj[symbol] = 'bar'
const anotherSymbol = Symbol.for('foo')
console.log(symbol === anotherSymbol) //=> true
console.log (obj [anotherSymbol]) //=> jbar
這在大型系統(tǒng)的開(kāi)發(fā)中可以用于一些全局的配罝數(shù)據(jù)中或者用于需要多處使用的數(shù)據(jù)中。
- 獲取全局Symbol的key
既然可以通過(guò)字符串的key在全局環(huán)境中注冊(cè)一個(gè)全局Symbol刀疙,那么同樣也可以根據(jù)這些全局的Symbol獲取到它們所對(duì)應(yīng)的key舶赔。
//Syntax:Symbol kefFor(<global symbol>):String
const symbol=Symbol.for('foobar')
console.log(Symbol.keyFor(symbol)) //=>foobar
(2)常用Symbol值
ES2015標(biāo)準(zhǔn)定義了一些內(nèi)置的常用Symbol值,這些Symbol值的應(yīng)用深入到了 ECMAScript引擎運(yùn)行中的各個(gè)角落庙洼。開(kāi)發(fā)者可以運(yùn)用這些常用Symbol值對(duì)代碼的內(nèi)部運(yùn)行邏輯進(jìn)行修改或拓展顿痪,以實(shí)現(xiàn)更高級(jí)的需求镊辕。
(3)Symbol.iterator
在ES2015標(biāo)準(zhǔn)中定義了可迭代對(duì)象(Iterable Object)和新的for-of循環(huán)語(yǔ)句油够,其中可迭代對(duì)象并不是一種類(lèi)型,而是帶有@@iterator屬性和可以被for-of循環(huán)語(yǔ)句所遍歷的對(duì)象的統(tǒng)稱(chēng)征懈。
for-of循環(huán)語(yǔ)句與可迭代對(duì)象
for-of循環(huán)語(yǔ)句是ES2015中新增的循環(huán)語(yǔ)句石咬,它可以對(duì)所有可迭代對(duì)象進(jìn)行遍歷,而不僅僅是數(shù)組卖哎。在ES2015中鬼悠,默認(rèn)的可迭代對(duì)象有:數(shù)組(Array)、字符串(String)亏娜、類(lèi)型數(shù)組(TypedArray)焕窝、映射對(duì)象(Map)、集合對(duì)象(Set)和生成器實(shí)例(Generator)维贺。
// Array
for (const el of [ 1, 2, 3 ]) console.log(el)
// String
for (const word of 'Hello World') console.log(word)
// TypedArray
for (const value of new Uint8Array([ 0x00, Oxff J)) console.log(value)
//Map
for (const entry of new Map ([ [' a', 1], [ 'b', 2]]) console.log (entry)
//Set
for (const el of new Set([ 1, 2, 3, 3, 3 ])) console.log (el)
// Generator
function* fn() { yield 1 }
for (const value of fn ()) console.log(value)
(4)Symbol.hasInstance
Symbol.haslnstance為開(kāi)發(fā)者提供了可以用于擴(kuò)展instanceof語(yǔ)句內(nèi)部邏輯的權(quán)限它掂,開(kāi)發(fā)者可以將其作為屬性
鍵,用于為一個(gè)類(lèi)定義靜態(tài)方法溯泣,該方法的第一個(gè)形參便是被檢測(cè)的對(duì)象虐秋,而該方法的返回值便是決定了當(dāng)次instanceof語(yǔ)句的返回結(jié)果。
class Foo (
static [Symbol.haslnstance](obj) {
console.log(obj) //=>{}
return true
}
}
console.log({} instanceof Foo) //=>true
(5)Symbol.match
Symbol.match是正則表達(dá)式(或者對(duì)象)在作為字符串使用match ()方法時(shí)垃沦,內(nèi)部運(yùn)行邏輯的自定義邏輯入口客给。開(kāi)發(fā)者可以通過(guò)Symbol.match來(lái)自行實(shí)現(xiàn)match ()方法的運(yùn)行邏輯,比如利用strcmp (在ECMAScript中為String.prototype.localeCompare())來(lái)實(shí)現(xiàn)肢簿。
const re = /foo/
re[Symbol.match]=function(str){
const regexp=this
console.log(str) //=>bar
//...
return true
}
'bar'.match(re) //=>true
(6)Symbol.toPrimitive
Symbol.toPrimitive為開(kāi)發(fā)者提供了更高級(jí)的控制權(quán)力靶剑,使得引用類(lèi)型的對(duì)象在轉(zhuǎn)換為值類(lèi)型時(shí)可以進(jìn)行自定義處理蜻拨,無(wú)論是轉(zhuǎn)換為字符串還是數(shù)字。
開(kāi)發(fā)者可以使用Symbol.toPrimitive作為屬性鍵為對(duì)象定義一個(gè)方法桩引,這個(gè)方法接受一個(gè)參數(shù)官觅,這個(gè)參數(shù)用于判斷當(dāng)前隱式轉(zhuǎn)換的目標(biāo)類(lèi)型。
需要注意的是阐污,這里的default并不是因?yàn)槟繕?biāo)類(lèi)型無(wú)法被轉(zhuǎn)換休涤,而是因?yàn)檎Z(yǔ)法上容易造成混亂。
(7)Symbol.toStringTag
常用Symbol的值在前面己經(jīng)提到過(guò)笛辟,它的作用是可以決定這個(gè)類(lèi)的實(shí)例在調(diào)用toString()方法時(shí)的標(biāo)簽內(nèi)容功氨。
在Object類(lèi)及其所有的子類(lèi)的實(shí)例中,有一個(gè)利用Symbol .toStringTag作為鍵的屬性手幢,該屬性定義著當(dāng)這個(gè)對(duì)象的toString()方法被調(diào)用時(shí)捷凄,所返回的Tag的內(nèi)容是什么。
比如在開(kāi)發(fā)者定義的類(lèi)中围来,就可以通過(guò)Symbol. toStringTag來(lái)修改toString()屮的標(biāo)簽內(nèi)容跺涤,利用它作為屬性鍵為類(lèi)型定義一個(gè)Getter。
class Bar {}
class Foo{
get [Symbol.toStringTagl() { return 'Bar'}
}
const obj =new Foo()
console.log(obj .toString() ) //=> [object Bar]
電子書(shū)鏈接: 《實(shí)戰(zhàn)ES2015:深入現(xiàn)代JavaScript+應(yīng)用開(kāi)發(fā)》 密碼: uetw