學(xué)習(xí)vue的語(yǔ)法之前进宝,首先要掌握一些ES6的新語(yǔ)法渠脉,以便更容易理解vue中的一些編程風(fēng)格叁熔。
1. 變量與常量的聲明
1.1 let關(guān)鍵字
1.1.1 let基本語(yǔ)法規(guī)則
ES6推薦使用let聲明局部變量蜜暑,相比之前的var(無(wú)論聲明在何處画恰,都會(huì)被視為聲明在函數(shù)的最頂部)
let和var聲明的區(qū)別可以理解為類似全局變量與局部變量的區(qū)別。
var x = '全局變量';
{
let x = '局部變量';
console.log(x); // 局部變量
}
console.log(x); // 全局變量
使用let聲明的變量綁定其所在的代碼塊删豺,在代碼塊之外的地方無(wú)法訪問(wèn)和使用
{
var x = 1;
let y = 2;
}
console.log(x); // 正常顯示1
console.log(y); //控制臺(tái)報(bào)錯(cuò):y is not defined
1.1.2 變量提升
如果使用var關(guān)鍵字聲明變量共虑,如下代碼執(zhí)行不會(huì)報(bào)錯(cuò),但會(huì)得到undefined結(jié)果呀页。
console.log(x); //顯示undefined
var x = 5;
此種情況被稱為變量提升
妈拌,可以理解為代碼在執(zhí)行時(shí)實(shí)際上是
var x;
console.log(x);
x = 5;
這樣做雖然方便但不符合一般編程語(yǔ)言的習(xí)慣,變量提升允許一個(gè)變量未聲明即可使用蓬蝶。
另一個(gè)變量提升
的示例
var tmp = new Date();
function f() {
console.log(tmp); //顯示為undefined
if (false) {
var tmp = "hello world";
}
}
f();
在調(diào)用f()函數(shù)時(shí)尘分,函數(shù)內(nèi)部在下方聲明了tmp,因此產(chǎn)生了變量提升的情況丸氛,先打印的tmp變?yōu)閡ndefined
使用let聲明變量時(shí)培愁,不支持變量提升
上述代碼如果更改為let聲明,控制臺(tái)會(huì)報(bào)錯(cuò)缓窜,提示變量x沒(méi)有定義(x is not defined
)
1.1.3 暫時(shí)性死區(qū)
只要塊級(jí)作用域內(nèi)存在let命令定续,它所聲明的變量就“綁定”(binding)這個(gè)區(qū)域,不再受外部的影響禾锤。
如果同時(shí)使用var和let進(jìn)行變量聲明私股,在代碼塊內(nèi)部let變量的優(yōu)先級(jí)要高于var變量。
這與一般編程語(yǔ)言局部變量?jī)?yōu)先級(jí)高于全局變量
的理解是一致的恩掷。
var x = 1;
if(true){
x = 3; //此處x指的是使用let聲明的局部變量庇茫。
let x;
}
上面代碼中,存在全局變量x螃成,但是塊級(jí)作用域內(nèi)let又聲明了一個(gè)局部變量x旦签,導(dǎo)致后者綁定這個(gè)塊級(jí)作用域查坪,所以在let聲明變量前,對(duì)x賦值會(huì)報(bào)錯(cuò)(x is not defined
)宁炫。
1.2 const關(guān)鍵字
const表示聲明常量偿曙,與let命令一致,也是一個(gè)塊級(jí)作用域命令羔巢。const的功能與Java編程語(yǔ)言中的final語(yǔ)法類似望忆。
const 聲明的變量都會(huì)被認(rèn)為是常量,意思就是它的值被設(shè)置完成后就不能再修改了
const x = 1
x = 0 //報(bào)錯(cuò)
如果const的是一個(gè)對(duì)象竿秆,對(duì)象所包含的值是可以被修改的启摄,不可改變的是對(duì)象的內(nèi)存地址。
const x = { name: 'cc' }
x.name = '趙四';// 不報(bào)錯(cuò)
x = { name: '趙四' };// 報(bào)錯(cuò)
x.name = '趙四';
不報(bào)錯(cuò)的的原因是只更改了對(duì)象的內(nèi)容幽钢,對(duì)象的內(nèi)存地址沒(méi)有改變
x = { name: '趙四' };
報(bào)錯(cuò)的原因是該句代碼相當(dāng)于重新在內(nèi)存新建了一個(gè)對(duì)象賦值給x歉备。
一般情況下,遵守編碼規(guī)范應(yīng)將常量名設(shè)置為全部大寫(xiě)
總結(jié)
- let 命令聲明的變量不具備
變量提升
特性 - let 和 const 是
塊級(jí)作用域
命令匪燕,只在其聲明所在的代碼塊中有效 - const 命令聲明常量時(shí)
必須賦值
2. 模板字符串
在ES6之前蕾羊,通過(guò)拼接字符串的方式來(lái)構(gòu)建文本輸出模板
比如:
let msg = {
name: '趙四',
age: 32,
job: '亞洲舞王'
};
var div = document.getElementById("divx");
div.innerHTML = "姓名: "+msg.name+"<br>年齡: "+msg.name+"<br>職業(yè): "+msg.name+"<br>";
ES6使用反引號(hào):``,配合EL語(yǔ)法${變量名}完成字符串拼接
上述示例使用ES6語(yǔ)法模板字符串完成如下
let msg = {
name: '趙四',
age: 32,
job: '亞洲舞王'
};
var div = document.getElementById("divx");
div.innerHTML =`姓名: ${msg.name}<br>年齡: ${msg.name}<br>職業(yè):${msg.name}<br>`;
總結(jié)
- 模板字符串使用反引號(hào)(`)表示字符串帽驯,不是引號(hào)
- 模板字符串使用EL語(yǔ)法
${...}
引用變量值替代原有的拼接方式
3. 函數(shù)聲明與傳參
3.1 參數(shù)默認(rèn)值
在ES6之前龟再,如果函數(shù)的參數(shù)在未參入?yún)?shù)時(shí)提供默認(rèn)值,需要如下方式實(shí)現(xiàn)
function printText(text) {
text = text || 'default'; //如果text為傳入數(shù)據(jù)尼变,則默認(rèn)設(shè)置為default
console.log(text);
}
ES6支持在函數(shù)聲明時(shí)直接指定參數(shù)默認(rèn)值利凑,上述示例修改如下
function printText(text = 'default') {
console.log(text);
}
3.2 Spread / Rest 操作符
Spread / Rest 操作符指的是...
,具體是 Spread 還是 Rest 需要看代碼的上下文嫌术“С海可以在函數(shù)調(diào)用/數(shù)組構(gòu)造時(shí), 將數(shù)組表達(dá)式或者string在語(yǔ)法層面展開(kāi);還可以在構(gòu)造對(duì)象時(shí), 將對(duì)象表達(dá)式按key-value的方式展開(kāi)蛉威。
3.2.1 Spread
當(dāng)...
被用于函數(shù)實(shí)參時(shí),它是一個(gè) Spread 操作符走哺。
let a = [1,2,3];
function f(x,y,z) {
console.log(x,y,z);
}
f(...a);
3.2.2 Rest
當(dāng)...
被用于函數(shù)形參時(shí)蚯嫌,它是一個(gè) Rest 操作符
function f(...x) {
console.log(x);
}
f(1,2,3,4,5);
3.3 箭頭函數(shù)
ES6中允許使用箭頭=>
定義函數(shù)
常規(guī)方式定義函數(shù)
var f1 = function (){
return 5;
}
var f2 = function(a, b){
return a+b;
}
var f3 = function(a, b){
console.log(a);
console.log(b);
return a+b;
}
箭頭函數(shù)
var f1 = () => 5;
var f2 = (a,b) => a+b;
var f3 = (a,b) => {
console.log(a);
console.log(b);
return a+b;
}
- 箭頭函數(shù)如果只有一行代碼,
=>
后直接書(shū)寫(xiě)即可 - 箭頭函數(shù)如果包含多行代碼丙躏,
=>
后編寫(xiě){}
择示,在代碼塊中完成函數(shù)代碼
由于{}
會(huì)被解析為代碼塊,如果箭頭函數(shù)返回的結(jié)果是一個(gè)對(duì)象晒旅,需要使用()
將對(duì)象的{}
括起來(lái)
var f = (a,b) => ({name : a, age : b});
除此之外栅盲,箭頭函數(shù)在使用中需要注意
- 函數(shù)體內(nèi)的this對(duì)象,是定義時(shí)所在的對(duì)象废恋,而不是使用時(shí)所在的對(duì)象谈秫。此項(xiàng)在vue開(kāi)發(fā)中十分關(guān)鍵扒寄,vue中箭頭函數(shù)的this對(duì)象是window,而常規(guī)函數(shù)的this對(duì)象是VUE對(duì)象
- 不可以當(dāng)作構(gòu)造函數(shù)拟烫,不可以使用new命令该编,否則拋出錯(cuò)誤。
- 不可以使用arguments對(duì)象硕淑,如果需要使用類似功能课竣,可以用Rest參數(shù)代替。
由此可見(jiàn)置媳,箭頭函數(shù)更適合一些邏輯簡(jiǎn)單于樟,只有少量代碼的函數(shù)編寫(xiě)。如果邏輯復(fù)雜代碼量較大拇囊,相對(duì)常規(guī)函數(shù)迂曲,箭頭函數(shù)并沒(méi)有優(yōu)勢(shì)。
總結(jié)
- 函數(shù)聲明時(shí)直接對(duì)形參進(jìn)行賦值寂拆,即相當(dāng)于對(duì)該形參設(shè)置默認(rèn)值
-
...
被用于函數(shù)實(shí)參
時(shí)奢米,它是一個(gè)Spread 操作符 -
...
被用于函數(shù)實(shí)參
時(shí),它是一個(gè) Rest 操作符 -
=>
函數(shù)可以簡(jiǎn)化函數(shù)聲明纠永,但它有一些需要注意的事項(xiàng)鬓长,比如this對(duì)象的指向
4. 二進(jìn)制與八進(jìn)制
ES6 支持二進(jìn)制和八進(jìn)制的字面量。
4.1 二進(jìn)制數(shù)據(jù)賦值
通過(guò)在數(shù)字前面添加0b
或者0B
表示二進(jìn)制值
let bValue = 0b10;
console.log(bValue); // 2
4.2 八進(jìn)制數(shù)據(jù)賦值
通過(guò)在數(shù)字前面添加0o
或者0O
表示八進(jìn)制值
let oValue = 0o10;
console.log(oValue); // 8
總結(jié)
- 使用
0b
或者0B
表示二進(jìn)制數(shù)據(jù) - 使用
0o
或者0O
表示二進(jìn)制數(shù)據(jù)
5. 對(duì)象與數(shù)組的解構(gòu)
ES6 允許按照一定模式尝江,從數(shù)組和對(duì)象中提取值涉波,對(duì)變量進(jìn)行賦值,這被稱為解構(gòu)(Destructuring)炭序。
5.1 數(shù)組的解構(gòu)
數(shù)組的元素是按次序排列的啤覆,變量的取值由它的位置決定,因此數(shù)組的解構(gòu)是位置相同
才能控制惭聂。
5.1.1 數(shù)組的解構(gòu)
比如常規(guī)聲明變量可以用如下方式
let a = 1;
let b = 2;
let c = 3;
也可以利用解構(gòu)方式聲明
let[a,b,c] = [1,2,3]
5.1.2 函數(shù)默認(rèn)參數(shù)中的數(shù)組解構(gòu)
使用解構(gòu)處理函數(shù)數(shù)組類型參數(shù)
function getRectDesc([width = 5, height = 5]) {
return `矩形的大小是:${width} x ${height}`;
}
此時(shí)調(diào)用函數(shù)可以用如下方式
getRectDesc([]); // 矩形的大小是:5 x 5
getRectDesc([2]); // 矩形的大小是:2 x 5
getRectDesc([2, 3]); // 矩形的大小是:2 x 3
getRectDesc([undefined, 3]); // 矩形的大小是:5 x 3
getRectDesc
函數(shù)預(yù)期傳入的是數(shù)組窗声。它通過(guò)解構(gòu)將數(shù)組中的第一項(xiàng)設(shè)為 width,第二項(xiàng)設(shè)為 height辜纲。如果數(shù)組為空笨觅,或者只有一項(xiàng),那么就會(huì)使用默認(rèn)參數(shù)耕腾,并將缺失的參數(shù)設(shè)為默認(rèn)值 5见剩。
但此時(shí)有個(gè)問(wèn)題,如果方法調(diào)用時(shí)沒(méi)有提供數(shù)組類型的實(shí)參扫俺,執(zhí)行時(shí)控制臺(tái)會(huì)出現(xiàn)異常苍苞。
getRectDesc(); // 控制臺(tái)拋出異常
因?yàn)?getRectDesc() 預(yù)期傳入的是數(shù)組,然后對(duì)其進(jìn)行解構(gòu)狼纬。因?yàn)楹瘮?shù)被調(diào)用時(shí)沒(méi)有傳入數(shù)組羹呵,所以出現(xiàn)問(wèn)題骂际。
此時(shí)解決辦法是繼續(xù)使用默認(rèn)的函數(shù)參數(shù)值
function getRectDesc([width = 5, height = 5] = []) {
return `矩形的大小是:${width} x ${height}`;
}
getRectDesc(); // 矩形的大小是:5 x 5
默認(rèn)的數(shù)組沒(méi)有提供任何值,此時(shí)解構(gòu)數(shù)組使用默認(rèn)的width和height担巩,所有結(jié)果是5x5
5.1.3 箭頭函數(shù)中的數(shù)組解構(gòu)
上一小節(jié)中的示例也可以使用箭頭函數(shù)來(lái)實(shí)現(xiàn)方援。
var getRectDesc = ([width = 5, height = 5] = []) => `矩形的大小是:${width} x ${height}`;
5.2 對(duì)象的解構(gòu)
對(duì)象的解構(gòu)與數(shù)組的解構(gòu)不同。數(shù)組的元素是按次序排列的涛癌,變量的取值由它的位置決定犯戏;而對(duì)象的屬性沒(méi)有次序,變量必須與屬性名相同
拳话,才能取到正確的值先匪。
5.2.1 對(duì)象的解構(gòu)
對(duì)象的解構(gòu)取決于變量與屬性名相同
,與順序無(wú)關(guān)弃衍。
let {job, name} = { name:"趙四", bar:"亞洲舞王"};
console.log(name); //趙四
console.log(job); //亞洲舞王
如果使用的變量名在屬性中不存在呀非,則變量沒(méi)有賦值,為undefined
let {age, name} = { name:"趙四", bar:"亞洲舞王"};
console.log(name); //趙四
console.log(age); //undefined
5.2.2 函數(shù)默認(rèn)參數(shù)中的對(duì)象解構(gòu)
使用解構(gòu)處理函數(shù)對(duì)象類型參數(shù)
function createArtist({sname = '趙四', job = '亞洲舞王'}){
return `${sname}的職業(yè)是${job}`;
}
此時(shí)調(diào)用函數(shù)可以用如下方式
createArtist({}); //趙四的職業(yè)是亞洲舞王
createArtist({sname:'劉能'}); //劉能的職業(yè)是亞洲舞王
createArtist({job: '喜劇演員'}); //趙四的職業(yè)是喜劇演員
createArtist({sname:'劉能',job:'喜劇演員'}); //劉能的職業(yè)是喜劇演員
createArtist
函數(shù)預(yù)期傳入的是對(duì)象镜盯。它通過(guò)解構(gòu)將對(duì)象中的屬性sname和job岸裙。如果對(duì)象為空,或者只有其中一個(gè)屬性賦值速缆,那么就會(huì)使用默認(rèn)參數(shù)降允,并將缺失的參數(shù)設(shè)為對(duì)應(yīng)的默認(rèn)值。
但此時(shí)有個(gè)問(wèn)題艺糜,如果方法調(diào)用時(shí)沒(méi)有提供對(duì)象類型的實(shí)參剧董,執(zhí)行時(shí)控制臺(tái)會(huì)出現(xiàn)異常。
createArtist(); // 控制臺(tái)拋出異常
因?yàn)?createArtist() 預(yù)期傳入的是對(duì)象破停,然后對(duì)其進(jìn)行解構(gòu)翅楼。因?yàn)楹瘮?shù)被調(diào)用時(shí)沒(méi)有傳入對(duì)象,所以出現(xiàn)問(wèn)題真慢。
此時(shí)解決辦法是繼續(xù)使用默認(rèn)的函數(shù)參數(shù)值
function createArtist({sname = '趙四', job = '亞洲舞王'} = {}){
return `${sname}的職業(yè)是${job}`;
}
createArtist(); //趙四的職業(yè)是亞洲舞王
默認(rèn)的對(duì)象沒(méi)有提供任何值毅臊,此時(shí)解構(gòu)對(duì)象使用默認(rèn)的sname和job,所有結(jié)果是“趙四的職業(yè)是亞洲舞王”
5.2.3 箭頭函數(shù)中的對(duì)象解構(gòu)
上一小節(jié)中的示例也可以使用箭頭函數(shù)來(lái)實(shí)現(xiàn)黑界。
var createArtist = ({sname = '趙四', job = '亞洲舞王'} = {}) => `${sname}的職業(yè)是${job}`;
總結(jié)
- 對(duì)
數(shù)組
進(jìn)行解構(gòu)操作時(shí)管嬉,依賴的關(guān)鍵是數(shù)據(jù)的位置
- 對(duì)
對(duì)象
進(jìn)行解構(gòu)操作時(shí),依賴的關(guān)鍵是屬性的名字
6. 循環(huán)與遍歷
6.1 for...in
for...in可以用于遍歷數(shù)組的下標(biāo)
let a = ['a', '123', {x: 1, y: 2}];
for(let key in a){
console.log(key); //0,1,2
}
for...in可以用于遍歷對(duì)象的屬性名
let a = {name:'趙四',job:'亞洲舞王', age: 23};
for(let key in a){
console.log(key);//name,job,age
}
6.2 for...of
for...of用于遍歷數(shù)組的數(shù)值
let a = ['a', '123', {x: 1, y: 2}];
for(let value of a){
console.log(value); //a, 123, {x:1, y:2}
}
for...in不能
用于遍歷對(duì)象的屬性名
园爷,控制臺(tái)會(huì)報(bào)錯(cuò)a is not iterable
let a = {name:'趙四',job:'亞洲舞王', age: 23};
for(let value of a){
console.log(value);//報(bào)錯(cuò)宠蚂! a is not iterable
}
總結(jié)
-
for...in
可以用于數(shù)組
和對(duì)象
的遍歷式撼,得到類似于key的數(shù)據(jù):數(shù)組的下標(biāo)
和對(duì)象的屬性名
-
for...of
可以用于數(shù)組
的遍歷童社,得到數(shù)組中的數(shù)值
,功能類似于Java語(yǔ)法中的for-each
句式 -
for...of
不能用于對(duì)象
數(shù)據(jù)的遍歷
7. 類的聲明與繼承
7.1 類的聲明
7.1.1 ES5標(biāo)準(zhǔn)中的類聲明
在ES5標(biāo)準(zhǔn)中沒(méi)有明確的關(guān)鍵字支持類著隆,需要利用原型鏈
來(lái)完成類的實(shí)現(xiàn)扰楼。
如下示例是一個(gè)關(guān)于鳥(niǎo)類的ES5實(shí)現(xiàn)
function Bird(name){
this.name = name;
}
Bird.prototype.fly = function (){
console.log(this.name+"正在飛");
}
var b = new Bird("燕子");
b.fly(); //燕子正在飛
其中name表示類中的一個(gè)屬性呀癣,fly為類中的一個(gè)常規(guī)方法。需要將fly方法掛載在prototype
上完成類的實(shí)現(xiàn)弦赖。
7.1.2 ES6標(biāo)準(zhǔn)中的類聲明
ES6 中支持 class
語(yǔ)法项栏,不過(guò),ES6的class不是新的對(duì)象繼承模型蹬竖,它只是原型鏈
的語(yǔ)法糖
表現(xiàn)形式沼沈。
上一小節(jié)中的示例使用ES6實(shí)現(xiàn)
class Bird{
constructor(name) {
this.name = name;
}
fly(){
console.log(`${this.name}正在飛`);
}
}
var b = new Bird("燕子");
b.fly(); //燕子正在飛
此處使用了class關(guān)鍵字聲明類,constructor關(guān)鍵字聲明構(gòu)造方法币厕,直接定義了fly方法
需要強(qiáng)調(diào)的是:雖然寫(xiě)法更接近面向?qū)ο?/code>式編程列另,但這些僅僅是
原型鏈
方式的語(yǔ)法糖,本質(zhì)上在底層實(shí)現(xiàn)上沒(méi)有變化旦装。
7.2 構(gòu)造方法
constructor
方法是類的默認(rèn)方法页衙,通過(guò)new命令生成對(duì)象實(shí)例時(shí)伦意,自動(dòng)調(diào)用該方法俊庇。
與Java
語(yǔ)言的語(yǔ)法類似,一個(gè)類必須有constructor方法撇寞,如果沒(méi)有顯式定義呻袭,系統(tǒng)會(huì)提供默認(rèn)的空構(gòu)造方法眨八。
constructor() {}
構(gòu)造方法默認(rèn)返回實(shí)例對(duì)象(即this),完全可以指定返回另外一個(gè)對(duì)象棒妨。
class Bird{
constructor() {
return Object.create(null);
}
}
new Bird() instanceof Bird// false
類的構(gòu)造函數(shù)踪古,必須通過(guò)創(chuàng)建對(duì)象的new關(guān)鍵字調(diào)用。這是它跟普通方法的一個(gè)主要區(qū)別券腔,后者不用new也可以執(zhí)行伏穆。
class Bird{
constructor() {
return Object.create(null);
}
}
Bird(); //控制臺(tái)拋出異常
7.3 對(duì)象
與ES5一樣纷纫,實(shí)例的屬性除非顯式定義在其本身(即定義在this對(duì)象上)枕扫,否則都是定義在原型上(即定義在class上)。
class Bird{
constructor(name) {
this.name = name;
}
fly() {
console.log(`${this.name}正在飛`);
}
}
var b = new Bird("燕子");
b.fly() // 燕子正在飛
console.log(b.hasOwnProperty('name')); // true
console.log(b.hasOwnProperty('fly')); // false
console.log(b.__proto__.hasOwnProperty('fly')); // true
上面代碼中辱魁,name是實(shí)例對(duì)象point自身的屬性(因?yàn)槎x在this變量上)烟瞧,
所以hasOwnProperty方法返回true,而toString是原型對(duì)象的屬性(因?yàn)槎x在Point類上)染簇,所以hasOwnProperty方法返回false参滴。
由此驗(yàn)證,類的所有實(shí)例共享一個(gè)原型對(duì)象锻弓,普通方法實(shí)際上是定義在原型上砾赔,被所有實(shí)例共享。
var p1 = new Point(2,3);
var p2 = new Point(3,2);
p1.__proto__ === p2.__proto__ //true
進(jìn)一步推導(dǎo)出,如果修改對(duì)象p1的原型暴心,同時(shí)會(huì)影響到p2妓盲,因?yàn)閮烧吖蚕硗粋€(gè)原型對(duì)象
p1.__proto__.printName = function () { return 'Oops' }; //為p1對(duì)象的原型新增一個(gè)printName函數(shù)
p1.printName() // "Oops"
p2.printName() // "Oops"
var p3 = new Point(4,2);
p3.printName() // "Oops"
可以看到當(dāng)為p1對(duì)象的原型增加一個(gè)printName方法時(shí),其余對(duì)象均可以使用這個(gè)方法专普。
這意味著悯衬,使用實(shí)例的__proto__
屬性改寫(xiě)原型,必須相當(dāng)謹(jǐn)慎檀夹,不推薦使用筋粗,因?yàn)檫@會(huì)改變class的原始定義,影響到所有實(shí)例炸渡。
如果將方法綁定在構(gòu)造方法中亏狰,可使方法綁定在this變量上而不是原型對(duì)象上
class Bird{
constructor(name) {
this.name = name;
this.fly = function (){
console.log(`${this.name}正在飛`);
}
}
}
var b = new Bird("燕子");
b.fly() // 燕子正在飛
console.log(b.hasOwnProperty('name')); // true
console.log(b.hasOwnProperty('fly')); // true
console.log(b.__proto__.hasOwnProperty('fly')); // false
7.4 繼承
同Java
語(yǔ)法中關(guān)于繼承的操作類似,ES6中允許使用extends
關(guān)鍵字完成繼承
class Bird{
constructor(name) {
this.name = name;
}
fly(){
console.log(`${this.name}正在飛`);
}
eat(){
console.log(`${this.name}正在吃`);
}
}
class Duck extends Bird{
constructor(name, color) {
super(name);
this.color = color;
}
swim(){
console.log(`${this.color}的${this.name}正在游泳`);
}
fly(){
super.fly();
console.log(`但${this.name}飛的不遠(yuǎn)`);
}
}
var b = new Bird("燕子");
var d = new Duck("大黃鴨","黃色");
b.eat(); //燕子正在吃
b.fly(); //燕子正在飛
d.eat(); //大黃鴨正在吃
d.fly(); //大黃鴨正在飛 但大黃鴨飛的不遠(yuǎn)
d.swim(); //黃色的大黃鴨正在游泳
繼承的特點(diǎn)是:
- extends 允許一個(gè)子類繼承父類偶摔,需要注意的是暇唾,子類的構(gòu)造方法中第一句需要執(zhí)行 super() 函數(shù)。
- 子類可以繼承父類的屬性和方法并直接調(diào)用使用辰斋,子類可以擴(kuò)展自己的屬性和方法
- 子類可以通過(guò)super關(guān)鍵字在子類方法中調(diào)用父類的方法策州,比如子類fly方法中的
super.fly()
。 - 子類可以重寫(xiě)父類的方法宫仗,比如Duck類中的fly方法
可以看到很多語(yǔ)法規(guī)則與Java語(yǔ)言的繼承語(yǔ)法幾乎一致
7.5 靜態(tài)
類相當(dāng)于實(shí)例的原型够挂,所有在類中定義的方法,都會(huì)被實(shí)例繼承藕夫。如果在一個(gè)方法前孽糖,加上static關(guān)鍵字,就表示該方法不會(huì)被實(shí)例繼承毅贮,而是直接通過(guò)類來(lái)調(diào)用办悟,這就稱為“靜態(tài)方法”
7.5.1 靜態(tài)方法
ES6中使用static
關(guān)鍵字描述靜態(tài)方法
class Bird{
constructor(name) {
this.name = name;
}
fly(){
console.log(`${this.name}正在飛`);
}
static show(){
console.log('鳥(niǎo)類擁有吃的技能');
}
}
var b = new Bird("燕子");
b.fly(); //燕子正在飛
Bird.show(); //鳥(niǎo)類擁有吃的技能
b.show(); //控制臺(tái)報(bào)錯(cuò) b.show is not a function
需要注意的是
- 靜態(tài)方法中不能使用this關(guān)鍵字訪問(wèn)屬性,其this指向的是鳥(niǎo)類的原型滩褥,也就是Bird病蛉。
- 子類可以繼承父類的靜態(tài)方法,也可以進(jìn)行重寫(xiě)瑰煎,但無(wú)論如何铺然,都不能使用對(duì)象去調(diào)用。
7.5.2 靜態(tài)屬性
ES6中無(wú)法使用static關(guān)鍵字設(shè)置靜態(tài)屬性酒甸,只能通過(guò)類名.靜態(tài)屬性名
方式聲明
class Bird{
...
}
Bird.number = 100;
console.log(Bird.number); //100
總結(jié)
- ES6關(guān)于類的操作實(shí)際上是一種
語(yǔ)法糖
魄健,本質(zhì)上底層實(shí)現(xiàn)依然依賴原型
-
constructor
表示構(gòu)造方法,其語(yǔ)法規(guī)則與普通方法相同插勤,但只能通過(guò)創(chuàng)建對(duì)象時(shí)沽瘦,使用關(guān)鍵字new
來(lái)調(diào)用
-
普通方法
的定義不用顯式寫(xiě)明掛載類的原型上柬甥,但本質(zhì)
上沒(méi)有變化
- 關(guān)于
對(duì)象
的一系列操作,與ES5相比沒(méi)有變化
其垄。 - 繼承的關(guān)鍵字是
extends
,規(guī)則與Java
語(yǔ)法中的繼承幾乎一致卤橄。 -
子類
的構(gòu)造方法第一句
必須調(diào)用父類構(gòu)造方法super()
绿满。 -
子類
可以繼承
父類的屬性
,方法
并可以在子類的內(nèi)部和子類的對(duì)象中使用 - 子類可以
重寫(xiě)
父類的方法窟扑,優(yōu)先執(zhí)行子類重寫(xiě)后的方法
8. 模塊化
8.1 使用模塊化的必要
歷史上喇颁,JavaScript 一直沒(méi)有模塊(module)體系,無(wú)法將一個(gè)大程序拆分成互相依賴的小文件嚎货,再用簡(jiǎn)單的方法拼裝起來(lái)橘霎。其他語(yǔ)言都有這項(xiàng)功能,比如 Ruby 的require殖属、Python 的import姐叁,甚至就連 CSS 都有@import,但是 JavaScript 任何這方面的支持都沒(méi)有洗显,這對(duì)開(kāi)發(fā)大型的外潜、復(fù)雜的項(xiàng)目形成了巨大障礙。
這是最原始的 JavaScript 文件加載方式挠唆,如果把每一個(gè)文件看做是一個(gè)模塊处窥,那么他們的接口通常是暴露在全局作用域下,也就是定義在 window 對(duì)象中玄组,不同模塊的接口調(diào)用都是一個(gè)作用域中滔驾,一些復(fù)雜的框架,會(huì)使用命名空間的概念來(lái)組織這些模塊的接口
<script src="module1.js"></script>
<script src="module2.js"></script>
<script src="libraryA.js"></script>
這種原始的加載方式暴露了一些顯而易見(jiàn)的弊端
- 全局作用域下容易造成變量沖突
- 文件只能按照<script> 的書(shū)寫(xiě)順序進(jìn)行加載
- 開(kāi)發(fā)人員必須主觀解決模塊和代碼庫(kù)的依賴關(guān)系
- 在大型項(xiàng)目中各種資源難以管理俄讹,長(zhǎng)期積累的問(wèn)題導(dǎo)致代碼庫(kù)混亂不堪
- window對(duì)象僅存在瀏覽器客戶端哆致,如此加載無(wú)法支持跨平臺(tái)
在 ES6 之前,社區(qū)制定了一些模塊加載方案患膛,最主要的有 AMD 和 CMD 兩種沽瞭。前者用于瀏覽器,后者用于服務(wù)器剩瓶。
8.2 模塊系統(tǒng)的演進(jìn)
8.2.1 AMD
對(duì)于依賴的模塊驹溃,AMD 是提前執(zhí)行。
AMD 的 API 默認(rèn)是一個(gè)當(dāng)多個(gè)用延曙,require 分全局 require 和局部 require豌鹤,都叫 require。
define("module", ["dep1", "dep2"], function(d1, d2) {// 依賴必須一開(kāi)始就寫(xiě)好
return someExportedValue;
});
require(["module", "../file"], function(module, file) { /* ... */ });
優(yōu)點(diǎn):
- 適合在瀏覽器環(huán)境中異步加載模塊
- 可以并行加載多個(gè)模塊
缺點(diǎn):
- 提高了開(kāi)發(fā)成本枝缔,代碼的閱讀和書(shū)寫(xiě)比較困難布疙,模塊定義方式的語(yǔ)義不順暢
- 不符合通用的模塊化思維方式蚊惯,是一種妥協(xié)的實(shí)現(xiàn)
8.2.2 CMD
對(duì)于依賴的模塊,CMD 是延遲執(zhí)行灵临。CMD 推崇 as lazy as possible截型。
CMD 的 API 嚴(yán)格區(qū)分,推崇職責(zé)單一儒溉,沒(méi)有全局 require宦焦,而是根據(jù)模塊系統(tǒng)的完備性,提供 seajs.use 來(lái)實(shí)現(xiàn)模塊系統(tǒng)的加載啟動(dòng)顿涣。
define(function(require, exports, module) {
var $ = require('jquery');
var Spinning = require('./spinning');// 依賴可以就近書(shū)寫(xiě)
exports.doSomething = ...
module.exports = ...
})
優(yōu)點(diǎn):
- 依賴就近波闹,延遲執(zhí)行
- 可以很容易在 Node.js 中運(yùn)行
缺點(diǎn):
- 依賴 SPM 打包,模塊的加載邏輯偏重
8.3 ES6的模塊指令
ES6 在語(yǔ)言標(biāo)準(zhǔn)的層面上涛碑,實(shí)現(xiàn)了模塊功能精堕,而且實(shí)現(xiàn)得相當(dāng)簡(jiǎn)單,完全可以取代 CMD 和 AMD 規(guī)范蒲障,成為瀏覽器和服務(wù)器通用的模塊解決方案歹篓。
ES6 模塊的設(shè)計(jì)思想,是盡量的靜態(tài)化揉阎,使得編譯時(shí)就能確定模塊的依賴關(guān)系滋捶,以及輸入和輸出的變量。
其具有以下特點(diǎn)
- 靜態(tài)加載模塊余黎,效率比CMD模塊的加載方式高
- ES6 模塊是編譯時(shí)加載重窟,使得靜態(tài)分析成為可能進(jìn)一步拓寬 JavaScript 的語(yǔ)法,比如引入宏(macro)和類型檢驗(yàn)(type system)這些只能靠靜態(tài)分析實(shí)現(xiàn)的功能惧财。
- 不再需要通用模塊(UMD)定義巡扇,將來(lái)服務(wù)器和瀏覽器都會(huì)支持 ES6 模塊格式。目前垮衷,通過(guò)各種工具庫(kù)厅翔,其實(shí)已經(jīng)做到了這一點(diǎn)
- 將來(lái)瀏覽器的新 API 就能用模塊格式提供,不再必須做成全局變量或者navigator對(duì)象的屬性搀突。
- 不再需要對(duì)象作為命名空間(比如Math對(duì)象)刀闷,未來(lái)這些功能可以通過(guò)模塊提供。
- 支持嚴(yán)格模式
模塊功能主要由兩個(gè)命令構(gòu)成:export
和import
仰迁。
-
export
命令用于規(guī)定模塊的對(duì)外接口甸昏。 -
import
命令用于輸入其他模塊提供的功能
在chrome中可以通過(guò)輸入chrome://flags/
指令打開(kāi)實(shí)驗(yàn)室,將Experimental JavaScript
變?yōu)?code>enabled
如果在html中引入模塊化的js文件徐许,需要設(shè)置
<script type="module" src="....">
8.3.1 export命令
export命令用于規(guī)定模塊的對(duì)外接口
下面代碼是dancer.js文件施蜜,保存了用戶信息。ES6 將其視為一個(gè)模塊雌隅,里面用export命令對(duì)外部輸出了三個(gè)變量翻默。
// dancer.js
export var name = '趙四';
export var job = '亞洲舞王';
export var age = 42;
另外一種寫(xiě)法缸沃,dancer.js文件,用export命令對(duì)外部輸出了三個(gè)變量組成的對(duì)象修械。
// dancer.js
var name = '趙四';
var job = '亞洲舞王';
var age = 42;
export{name,job,age};
export命令可以輸出函數(shù)趾牧,允許同時(shí)輸出多個(gè)函數(shù)
下面示例輸出了compute.js的multiply和addition函數(shù)
//compute.js
export function multiply(x, y) {
return x * y;
};
export function addition(x, y) {
return x + y;
}
對(duì)輸出函數(shù)進(jìn)行重命名,使用as
關(guān)鍵字進(jìn)行重命名
//compute.js
function multiply(x, y) {
return x * y;
};
function addition(x, y) {
return x + y;
}
export {
multiply as cheng,
addition as jia
}
export命令可以輸出類
下方示例輸出bird.js中的Bird類
//bird.js
export class Bird{
constructor(name) {
this.name = name;
}
fly() {
console.log(`${this.name}正在飛`);
}
}
export default 命令
很多時(shí)候肯污,一個(gè)js文件中可能輸出很多內(nèi)容翘单,為了方便使用者import,可以使用export default
指定默認(rèn)的輸出內(nèi)容
下方示例test.js中默認(rèn)export的匿名函數(shù)
//test.js
export default function () {
console.log('foo');
}
也可以指定非匿名函數(shù)
下方示例test.js中默認(rèn)export的foo函數(shù)
//test.js
export default function foo() {
console.log('foo');
}
或
//test.js
function foo() {
console.log('foo');
}
export default foo;
8.3.2 import命令
使用export命令定義了模塊的對(duì)外接口以后仇箱,其他 JS 文件就可以通過(guò)import命令加載這個(gè)模塊
import對(duì)象或變量
// 加載dancer.js
import { name, job, age } from './dancer.js‘
console.log(`${name},${job},${age}`);
利用as
創(chuàng)建別名
// 加載dancer.js
import { name, job, age as nl } from './dancer.js‘
console.log(`${name},${job},${nl}`);
import命令加載函數(shù)
import {multiply} from './compute.js'
console.log(multiply(3,4));
import命令加載類
//main.js
import {Bird} from './bird.js'
var b = new Bird('燕子');
b.eat();
import命令具有提升效果,會(huì)提升到整個(gè)模塊的頭部东羹,首先執(zhí)行
下面的代碼不會(huì)報(bào)錯(cuò)剂桥,因?yàn)閕mport的執(zhí)行早于foo的調(diào)用。這種行為的本質(zhì)是属提,import命令是編譯階段執(zhí)行的权逗,在代碼運(yùn)行之前。
foo();
import { foo } from './test.js';
import是靜態(tài)執(zhí)行冤议,所以不能使用表達(dá)式和變量斟薇,這些只有在運(yùn)行時(shí)才能得到結(jié)果的語(yǔ)法結(jié)構(gòu)。
下面三種寫(xiě)法都會(huì)報(bào)錯(cuò)恕酸,因?yàn)樗鼈冇玫搅吮磉_(dá)式堪滨、變量和if結(jié)構(gòu)。在靜態(tài)分析階段蕊温,這些語(yǔ)法都是沒(méi)法得到值的袱箱。
// 報(bào)錯(cuò)
import { 'f' + 'oo' } from ‘./test.js';
// 報(bào)錯(cuò)
let module = ‘./test.js';
import { foo } from module;
// 報(bào)錯(cuò)
if (x === 1) {
import { foo } from './test1.js';
} else {
import { foo } from './test2.js';
}
import語(yǔ)句是 Singleton 模式。次重復(fù)執(zhí)行同一句import語(yǔ)句义矛,那么只會(huì)執(zhí)行一次发笔,而不會(huì)執(zhí)行多次
import { name} from './dancer.js';
import { age } from './dancer.js';
// 等同于
import { name, age } from './dancer.js';
除了指定加載某個(gè)輸出值,還可以使用整體加載凉翻,即用星號(hào)(*)指定一個(gè)對(duì)象了讨,所有輸出值都加載在這個(gè)對(duì)象上面。
// circle.js
export function area(radius) {
return Math.PI * radius * radius;
}
export function round(radius) {
return 2 * Math.PI * radius;
}
逐一加載
// main.js逐一指定要加載的方法
import { area, round} from './circle.js';
console.log('圓面積:' + area(4));
console.log('圓周長(zhǎng):' + round(14));
整體加載
//main.js整體加載
import * as circle from './circle.js';
console.log('圓面積:' + circle.area(4));
console.log('圓周長(zhǎng):' + circle.round(14));
總結(jié)
- 模塊化是支持Javascript能夠完成大型復(fù)雜結(jié)構(gòu)項(xiàng)目的基石
- ES6中通過(guò)
export
和import
指令完成模塊的導(dǎo)出和引入 -
export
命令可以導(dǎo)出變量制轰,對(duì)象前计,函數(shù)和類 -
import
命令用于引入導(dǎo)出的變量,對(duì)象垃杖,函數(shù)和類 -
export
和import
屬于靜態(tài)操作残炮,在編譯時(shí)完成,不能使用運(yùn)行時(shí)語(yǔ)法(比如變量缩滨,條件控制等) -
import
命令具有提升效果势就,會(huì)提升到整個(gè)模塊的頭部泉瞻,首先執(zhí)行
9. Promise對(duì)象
Promise 對(duì)象用于一個(gè)異步操作的最終完成(或失敗)及其結(jié)果值的表示苞冯。簡(jiǎn)單點(diǎn)說(shuō)袖牙,它就是用于處理異步操作的,異步處理成功了就執(zhí)行成功的操作舅锄,異步處理失敗了就捕獲錯(cuò)誤或者停止后續(xù)操作鞭达。
9.1 使用Promise對(duì)象的優(yōu)勢(shì)
接觸過(guò)Node的人都知道,Node是以異步(Async)回調(diào)著稱的皇忿,其異步性提高了程序的執(zhí)行效率畴蹭,但同時(shí)也減少了程序的可讀性。如果我們有幾個(gè)異步操作鳍烁,并且后一個(gè)操作需要前一個(gè)操作返回的數(shù)據(jù)才能執(zhí)行叨襟,這樣按照Node的一般執(zhí)行規(guī)律,要實(shí)現(xiàn)有序的異步操作幔荒,通常是一層加一層的回調(diào)函數(shù)嵌套下去糊闽,這種情況被稱為回調(diào)地獄
。
Promise是異步編程的一種解決方案爹梁,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更強(qiáng)大右犹。所謂Promise,簡(jiǎn)單說(shuō)就是一個(gè)容器姚垃,里面保存著某個(gè)未來(lái)才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果念链。從語(yǔ)法上說(shuō),Promise是一個(gè)對(duì)象积糯,從它可以獲取異步操作的消息钓账。Promise提供統(tǒng)一的API,各種異步操作都可以用同樣的方法進(jìn)行處理絮宁。
有了Promise對(duì)象梆暮,就可以將異步操作以同步操作的流程表達(dá)出來(lái),避免了層層嵌套的回調(diào)函數(shù)绍昂。此外啦粹,Promise對(duì)象提供統(tǒng)一的接口,使得控制異步操作更加容易窘游。
Promise也有一些缺點(diǎn)唠椭。首先,無(wú)法取消Promise忍饰,一旦新建它就會(huì)立即執(zhí)行贪嫂,無(wú)法中途取消。其次艾蓝,如果不設(shè)置回調(diào)函數(shù)力崇,Promise內(nèi)部拋出的錯(cuò)誤斗塘,不會(huì)反應(yīng)到外部。第三亮靴,當(dāng)處于pending狀態(tài)時(shí)馍盟,無(wú)法得知目前進(jìn)展到哪一個(gè)階段(剛剛開(kāi)始還是即將完成)。
9.2 Promise三種操作的狀態(tài)
對(duì)于Promise對(duì)象來(lái)說(shuō)茧吊,它也有三種狀態(tài):
- pending:初始狀態(tài),也稱為未定狀態(tài)贞岭,就是初始化Promise時(shí),調(diào)用executor執(zhí)行器函數(shù)后的狀態(tài)搓侄。
- fulfilled:完成狀態(tài)瞄桨,意味著異步操作成功。
- rejected:失敗狀態(tài)讶踪,意味著異步操作失敗芯侥。
它只有兩種狀態(tài)可以轉(zhuǎn)化,即
- 操作成功:pending -> fulfilled
- 操作失斂∪帷:pending -> rejected
并且這個(gè)狀態(tài)轉(zhuǎn)化是單向的筹麸,不可逆轉(zhuǎn)活合,已經(jīng)確定的狀態(tài)(fulfilled/rejected)無(wú)法轉(zhuǎn)回初始狀態(tài)(pending)雏婶。
9.3 Promise的使用
const promise = new Promise((resolve, reject) => {
// do something here ...
if (success) {
resolve(value); // fulfilled 可以理解為是成功的回調(diào)函數(shù)
} else {
reject(error); // rejected 可以理解為是失敗的回調(diào)函數(shù)
}
});
由上述代碼我們可知:
- 該構(gòu)造函數(shù)接收兩個(gè)函數(shù)作為參數(shù),分別是resolve和reject白指。
- 當(dāng)異步操作執(zhí)行成功后留晚,會(huì)將異步操作結(jié)果作為參數(shù)傳入resolve函數(shù)并執(zhí)行,此時(shí) Promise對(duì)象狀態(tài)從pending變?yōu)閒ulfilled告嘲;
- 失敗則會(huì)將異步操作的錯(cuò)誤作為參數(shù)傳入reject函數(shù)并執(zhí)行错维,此時(shí) Promise對(duì)象狀態(tài)從pending變?yōu)閞ejected;
接下來(lái)橄唬,
我們通過(guò)then方法赋焕,分別指定resolved狀態(tài)和rejected狀態(tài)的回調(diào)函數(shù)
promise.then(function(value) {
// success
}, function(error) {
// failure
});
then方法可以接收兩個(gè)回調(diào)函數(shù)作為參數(shù)。
- 第一個(gè)回調(diào)函數(shù)就是fulfilled狀態(tài)時(shí)調(diào)用仰楚;
- 第二個(gè)回調(diào)函數(shù)就是rejected時(shí)調(diào)用隆判,可選
9.4 Promise的API
9.4.1 Promise.all(iterable)
Promise.all可以將多個(gè)Promise實(shí)例包裝成一個(gè)新的Promise實(shí)例。同時(shí)僧界,成功和失敗的返回值是不同的侨嘀,成功的時(shí)候返回的是一個(gè)結(jié)果數(shù)組,而失敗的時(shí)候則返回最先被reject失敗狀態(tài)的值捂襟。
let p1 = new Promise((resolve, reject) => {
resolve('成功了')
})
let p2 = new Promise((resolve, reject) => {
resolve('success')
})
let p3 = Promse.reject('失敗')
Promise.all([p1, p2]).then((result) => {
console.log(result) //['成功了', 'success']
}).catch((error) => {
console.log(error)
})
Promise.all([p1,p3,p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 失敗了咬腕,打出 '失敗'
})
Promse.all在處理多個(gè)異步處理時(shí)非常有用,比如說(shuō)一個(gè)頁(yè)面上需要等兩個(gè)或多個(gè)ajax的數(shù)據(jù)回來(lái)以后才正常顯示葬荷,在此之前只顯示loading圖標(biāo)涨共。
let wake = (time) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${time / 1000}秒后醒來(lái)`);
console.log(`${time} is over`);
}, time)
})
}
let p1 = wake(3000)
let p2 = wake(2000)
Promise.all([p1, p2]).then((result) => {
console.log(result) // [ '3秒后醒來(lái)', '2秒后醒來(lái)' ]
}).catch((error) => {
console.log(error)
})
需要特別注意的是
- 請(qǐng)求的過(guò)程是異步的纽帖,也就是先執(zhí)行了p2的打印語(yǔ)句(2000 is over),再執(zhí)行了p1的打印語(yǔ)句(3000 is over)
- Promise.all獲得的成功結(jié)果的數(shù)組里面的數(shù)據(jù)順序和Promise.all接收到的數(shù)組順序是一致的,即p1的結(jié)果在前煞赢,即便p1的結(jié)果獲取的比p2要晚抛计。
9.4.2 Promise.race(iterable)
顧名思義,Promse.race就是賽跑的意思照筑,意思就是說(shuō)吹截,Promise.race([p1, p2, p3])里面哪個(gè)結(jié)果獲得的快,就返回那個(gè)結(jié)果凝危,不管結(jié)果本身是成功狀態(tài)還是失敗狀態(tài)波俄。
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
},1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('failed')
}, 500)
})
Promise.race([p1, p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 打開(kāi)的是 'failed'
})
9.4.3 Promise.resolve(value)
有時(shí)需要將現(xiàn)有對(duì)象轉(zhuǎn)為Promise對(duì)象,Promise.resolve方法就起到這個(gè)作用蛾默。
將jQuery生成的deferred對(duì)象懦铺,轉(zhuǎn)為一個(gè)新的Promise對(duì)象。
const jsPromise = Promise.resolve($.ajax('/whatever.json'));
Promise.resolve等價(jià)于下面的寫(xiě)法支鸡。
Promise.resolve('foo')冬念;
// 等價(jià)于
new Promise(resolve => resolve('foo'));
reslolve參數(shù)有以下四種情況
- 參數(shù)是一個(gè)Promise實(shí)例牧挣,promise.resolve將不做任何修改急前、原封不動(dòng)地返回這個(gè)實(shí)例。
- 參數(shù)是一個(gè)thenable對(duì)象瀑构,thenable對(duì)象指的是具有then方法的對(duì)象
比如
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
Promise.resolve方法會(huì)將這個(gè)對(duì)象轉(zhuǎn)為Promise對(duì)象裆针,然后就立即執(zhí)行thenable對(duì)象的then方法。
- 參數(shù)時(shí)不具備then方法的對(duì)象或者根部不是對(duì)象寺晌,如果參數(shù)是一個(gè)原始值世吨,或者是一個(gè)不具有then方法的對(duì)象,則Promise.resolve方法返回一個(gè)新的Promise對(duì)象呻征,狀態(tài)為resolved耘婚。
- 不帶有任何參數(shù),直接返回一個(gè)resolved狀態(tài)的Promise對(duì)象陆赋。
所以沐祷,如果希望得到一個(gè)Promise對(duì)象,比較方便的方法就是直接調(diào)用Promise.resolve方法奏甫。
const p = Promise.resolve();
p.then(function () {
// ...
});
9.4.4 Promise.reject()
返回一個(gè)新的Promise實(shí)例戈轿,該實(shí)例的狀態(tài)為rejected。
const p = Promise.reject('出錯(cuò)了');
// 等同于
const p = new Promise((resolve, reject) => reject('出錯(cuò)了'))
p.then(null, function (s) {
console.log(s)
});
9.4.5 Promise.Promise.prototype.then()
Promise 的實(shí)例具有 then 方法阵子,主要作用是為 Promise 實(shí)例發(fā)生狀態(tài)改變時(shí)添加回調(diào)函數(shù)思杯。
它接收兩個(gè)回調(diào)函數(shù)作為參數(shù),第一個(gè)參數(shù)是 fulfilled狀態(tài)時(shí)的回調(diào)函數(shù);第二個(gè)參數(shù)是rejected狀態(tài)時(shí)的回調(diào)函數(shù)色乾,可不傳入誊册。并且該方法返回一個(gè)新的Promise對(duì)象。
p.then(onResolve, onReject);
p.then(function(value) {
// fulfillment
}, function(reason) {
// rejection
});
9.4.6 Promise.Promise.prototype.catch()
返回一個(gè)Promise暖璧,并且處理拒絕的情況案怯。它的行為與調(diào)用Promise.prototype.then(undefined, onRejected)相同。推薦使用catch方法澎办,不要在then方法中定義rejected狀態(tài)的回調(diào)函數(shù)嘲碱;這是因?yàn)槭褂胏atch還可以捕獲在then方法執(zhí)行中存在的錯(cuò)誤。
p.catch(onReject)
p.catch(function(reason) {
// 拒絕
});
9.4.7 Promise.Promise.prototype.finally()
返回一個(gè)Promsie局蚀。是指麦锯,在上一輪 promise 運(yùn)行結(jié)束后,無(wú)論fulfilled還是 rejected琅绅,都會(huì)執(zhí)行指定的回調(diào)函數(shù)扶欣。該方法適合無(wú)論結(jié)果如何都要進(jìn)行的操作,例如清除數(shù)據(jù)千扶。finally不接收任何參數(shù)料祠。
p.finally(onFinally);
p.finally(function() {
})
9.5 Promise對(duì)象實(shí)例演示
9.5.1 加載圖片
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script>
let preloadImg = function (img, path) {
return new Promise(function (resolve, reject) {
img.onload = resolve;
img.onerror = reject;
img.src = path;
});
};
let img1 = new Image();
let p1 = preloadImg(img1, 'img/1.jpg')
p1.then(function (){
console.log('圖片加載成功');
document.getElementById("div1").appendChild(img1);
}).catch(function (){
document.getElementById("div1").innerHTML = "圖片加載失敗";
});
let img2 = new Image();
let p2 = preloadImg(img2, 'img/1.txt');
p2.then(function (){
console.log('圖片加載成功');
document.getElementById("div2").appendChild(img2);
}).catch(function (){
document.getElementById("div2").innerHTML = "圖片加載失敗";
});
</script>
</head>
<body>
<div id="div1"></div>
<div id="div2"></div>
</body>
</html>
9.5.2 鏈?zhǔn)郊虞d
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script>
function runAsync1(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('異步任務(wù)執(zhí)行完成1');
resolve('數(shù)據(jù)1');
}, 1000);
});
return p;
}
function runAsync2(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('異步任務(wù)執(zhí)行完成2');
resolve('數(shù)據(jù)2');
}, 5000);
});
return p;
}
function runAsync3(){
var p = new Promise(function(resolve, reject){
//做一些異步操作
setTimeout(function(){
console.log('異步任務(wù)執(zhí)行完成3');
resolve('數(shù)據(jù)3');
}, 3000);
});
return p;
}
runAsync1()
.then(function(data){
console.log(data);
return runAsync2();
})
.then(function(data){
console.log(data);
return runAsync3();
})
.then(function(data){
console.log(data);
});
</script>
</head>
<body>
</body>
</html>