前序 ES6的新語(yǔ)法

學(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)成:exportimport仰迁。

  • 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ò)exportimport指令完成模塊的導(dǎo)出和引入
  • export命令可以導(dǎo)出變量制轰,對(duì)象前计,函數(shù)和類
  • import命令用于引入導(dǎo)出的變量,對(duì)象垃杖,函數(shù)和類
  • exportimport屬于靜態(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)地獄

回調(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>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市澎羞,隨后出現(xiàn)的幾起案子髓绽,更是在濱河造成了極大的恐慌,老刑警劉巖煤痕,帶你破解...
    沈念sama閱讀 212,222評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梧宫,死亡現(xiàn)場(chǎng)離奇詭異接谨,居然都是意外死亡摆碉,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)脓豪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)巷帝,“玉大人,你說(shuō)我怎么就攤上這事扫夜±闫茫” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,720評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵笤闯,是天一觀的道長(zhǎng)堕阔。 經(jīng)常有香客問(wèn)我,道長(zhǎng)颗味,這世上最難降的妖魔是什么超陆? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,568評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮浦马,結(jié)果婚禮上时呀,老公的妹妹穿的比我還像新娘张漂。我一直安慰自己,他們只是感情好谨娜,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,696評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布航攒。 她就那樣靜靜地躺著,像睡著了一般趴梢。 火紅的嫁衣襯著肌膚如雪漠畜。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,879評(píng)論 1 290
  • 那天坞靶,我揣著相機(jī)與錄音盆驹,去河邊找鬼。 笑死滩愁,一個(gè)胖子當(dāng)著我的面吹牛躯喇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播硝枉,決...
    沈念sama閱讀 39,028評(píng)論 3 409
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼廉丽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了妻味?” 一聲冷哼從身側(cè)響起正压,我...
    開(kāi)封第一講書(shū)人閱讀 37,773評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎责球,沒(méi)想到半個(gè)月后焦履,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,220評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡雏逾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,550評(píng)論 2 327
  • 正文 我和宋清朗相戀三年嘉裤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片栖博。...
    茶點(diǎn)故事閱讀 38,697評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡屑宠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仇让,到底是詐尸還是另有隱情典奉,我是刑警寧澤,帶...
    沈念sama閱讀 34,360評(píng)論 4 332
  • 正文 年R本政府宣布丧叽,位于F島的核電站卫玖,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏踊淳。R本人自食惡果不足惜假瞬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,002評(píng)論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧笨触,春花似錦懦傍、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,782評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至虚吟,卻和暖如春寸认,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背串慰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,010評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工偏塞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人邦鲫。 一個(gè)月前我還...
    沈念sama閱讀 46,433評(píng)論 2 360
  • 正文 我出身青樓灸叼,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親庆捺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子古今,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,587評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • [TOC] 參考阮一峰的ECMAScript 6 入門(mén)參考深入淺出ES6 let和const let和const都...
    郭子web閱讀 1,773評(píng)論 0 1
  • 第一章:塊級(jí)作用域綁定 塊級(jí)聲明 1.var聲明及變量提升機(jī)制:在函數(shù)作用域或者全局作用域中通過(guò)關(guān)鍵字var聲明的...
    BeADre_wang閱讀 825評(píng)論 0 0
  • 以下內(nèi)容是我在學(xué)習(xí)和研究ES6時(shí),對(duì)ES6的特性滔以、重點(diǎn)和注意事項(xiàng)的提取捉腥、精練和總結(jié),可以做為ES6特性的字典你画;在本...
    科研者閱讀 3,120評(píng)論 2 9
  • ES6簡(jiǎn)介 ECMAScript 6.0(以下簡(jiǎn)稱 ES6)是 JavaScript 語(yǔ)言的下一代標(biāo)準(zhǔn)抵碟,已經(jīng)在 2...
    凌雨微塵閱讀 1,067評(píng)論 0 3
  • 1、新的聲明方式 以前我們?cè)诼暶鲿r(shí)只有一種方法坏匪,就是使用var來(lái)進(jìn)行聲明拟逮,ES6對(duì)聲明的進(jìn)行了擴(kuò)展,現(xiàn)在可以有三種...
    令武閱讀 1,001評(píng)論 0 7