ES6+知識點梳理及背後原理+工做中推薦用法

前言

本文將介紹ES2016以及以後版本中經常使用的知識點以及該知識點與以前版本用法的對比,更多的瞭解知識點背後的原理,從而更加深入的理解ES6+到底好在哪裏。前端

一、let和const


let,const都是用來聲明變量,用來替代老語法的var關鍵字,與var不一樣的是,它們會建立一個塊級做用域(通常一個花括號內是一個新的做用域)
特色:
  • 不存在變量提高
  • 不容許重複聲明
  • 暫時性的死區——只要塊級做用域內用let,const聲明的變量,那這個變量就綁定到這個區域了,不受外部的影響
  • 塊級做用域
  • let和var全局聲明時,var能夠經過window的屬性訪問而let不能。
  • const聲明的變量必須賦值,即不能只聲明不初始化
  • const聲明的變量不能改變,若是聲明的變量是引用類型(數組或對象),則不能改變它的內存地址
場景案例:

場景1:經典面試題vue

// 使用var
for (var i = 0; i < 5; i++) {
    setTimeout(function () {
        console.log(i);
    });
} // => 5 5 5 5 5

// 使用let
for (let i = 0; i < 5; i++) {
    setTimeout(function () {
        console.log(i);
    });
} // => 0 1 2 3 4

知識點:node

  1. for循環:設置循環變量的部分(即圓括號部分)是一個父做用域,而循環體內部(即中括號部分)是一個單獨的子做用域,因此能夠在循環體內部訪問到i的值。
  2. 變量i:使用var聲明的變量i,在預編譯階段會有變量提高,會提高到當前做用域的頂部並初始化賦值爲undefined,var i=0在for循環中只執行一次;使用let聲明的變量i,不存在變量提高,只有代碼運行到let i語句的時候纔開始初始化並賦值,在for循環中當前的i只在本輪循環中有用,能夠理解爲每次循環的i其實都是一個新的變量,JS內部引擎會記住上次循環結果賦值給下個循環。
  3. setTimeout:會將一個匿名的回調函數推入異步隊列,而且回調函數還具備全局性,在非嚴格模式下this會指向window。這裏就會有這樣一個問題:setTimeout裏的函數是什麼時候進入任務隊列?,請看下面的代碼:
setTimeout(function () {
    console.log('ab')
}, 5 * 1000) //這裏改成0ms
for (let i = 0; i <= 5000000; i++) {
    if (i === 5000000) {
        console.log(i)
    }
}

說明:setTimeout裏的函數在setTimeout執行的時候,就開始計時,計時完成(這裏就是5s以後)才進入任務隊列,當主執行棧執行完就開始執行任務隊列。es6

場景2:const使用面試

const a; //Uncaught SyntaxError: Missing initializer in const declaration
const a=1;
a=2; //Uncaught TypeError: Assignment to constant variable.
const obj = {
    a: 1
}
obj.a = 2
obj = { b: 1 } //Uncaught TypeError: Assignment to constant variable.
let a = 1
console.log(window.a)//undefined
var b=1;
console.log(window.b)//1

二、解構賦值


解構賦值主要就是 數組的解構賦值和 對象的解構賦值,至於其餘數據類型的解構賦值都與這兩種有關係,對象的解構賦值的內部機制,是先找到 同名屬性,而後再賦給對應的變量。真正被賦值的是後者,而不是前者。

注意點:ajax

  1. 數組的元素是按次序排列的,變量的取值由它的位置決定;而對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值。
  2. 只要等號右邊的值不是對象或數組,就先將其轉爲對象。因爲undefined和null沒法轉爲對象,因此對它們進行解構賦值,都會報錯。
  3. 對象的解構賦值能夠取到繼承的屬性。
對象的解構賦值

場景1:內部機制vuex

let {
    name: nameOne,
    test: [{
        name: nameTwo
    }]
} = {
    name: 'wx',
    test: [{
        name: 'test'
    }]
}
//簡化版
let { name, test } = {
    name: 'wx',
    test: [{
        name: 'test'
    }]
}

說明:這裏等號左邊真正聲明的變量實際上是nameOnenameTwo,而後根據它們所對應的位置找到等號右邊對應的值,從而進行賦值。編程

場景2:對象的方法json

// 例一
let { sin, cos } = Math;

// 例二
const { log } = console;
log('hello') // hello

//vuex中action中的方法
//不使用對象解構:
const actions = {
    getMainTaskList(ctx) {
        ctx.commit('setMainTask')
    },
}
//使用對象解構:
const actions = {
    getMainTaskList({ commit, state, dispatch }) {
        commit('setMainTask', { mainTaskData: JSON.parse(res) })
    },
}

場景3:嵌套解構的對象segmentfault

let obj = {
    p: [
        'Hello',
        { y: 'World' }
    ]
};
// 之前的寫法:
let x = obj.p[0]
let y = obj.p[1].y

// 解構寫法:
let { p: [x, { y }] } = obj;
x // "Hello"
y // "World"

場景4:繼承的原型屬性

const obj1 = {}
const obj2 = { foo: 'bar' }
Object.setPrototypeOf(obj1, obj2)//ES6推薦設置原型方法

const { foo } = obj1
foo // "bar"
數組的解構賦值
// 老式寫法
const local = 'wx-18'
const splitLocale = locale.split("-");
const language = splitLocale[0];
const country = splitLocale[1];

// 解構賦值寫法
const [language, country] = locale.split('-');
其餘類型的解構賦值
//字符串
const [a, b, c, d, e] = 'hello';

//數值和布爾值
let {toString: s} = 123;
s === Number.prototype.toString // true
console.log(s);//function toString()
//[查看詳細的解釋](https://segmentfault.com/q/1010000005647566)

//undefined和null
注意:因爲`undefined`和`null`沒法轉爲對象,因此對它們進行解構賦值,都會報錯。

三、擴展運算符


擴展運算符主要是兩大類:數組和對象的擴展運算符。使用三個點點(...),後面跟含有 iterator接口的數據結構
數組的擴展運算符

場景1:複製數組

// ES5的複製數組-複製了指針(淺)
const a1 = [1, 2];
const a2 = a1;
a2[0] = 2;

// ES5的複製數組-深拷貝
const a1 = [1, 2];
const a2 = a1.concat();//a1.slice()
a2[0] = 2;

// ES6複製數組-深拷貝
const a1 = [1,2]
const a2 = [...a1] //const [...a2] = a1
a2[0] = 2;

場景2:合併數組

//ES5合併數組
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3  = arr1.concat(arr2) //["a", "b", "c"]

// ES6合併數組
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3  = [...arr1,...arr2] //["a", "b", "c"]

場景3:與解構賦值結合使用

const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first) //1
console.log(rest) //[2, 3, 4, 5]

//將擴展運算符用於數組賦值,只能放在參數的最後一位,不然會報錯
const [...rest,last] = [1, 2, 3, 4, 5];//Uncaught SyntaxError: Rest element must be last element

場景4:類對象轉數組

任何定義了遍歷器( Iterator)接口的對象,均可以用擴展運算符轉爲真正的數組。
// 例1:
let nodeList = document.querySelectorAll('div');//類數組對象,而且部署了iterator接口
let arr = [...nodeList];

// 例2:
let arrayLike = {
    '0': 'a',
    '1': 'b',
    length: 2
};
// Uncaught TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))
let arr = [...arrayLike];

//固然能夠經過`Array.from(arrayLike)`將它轉爲真正的數組
let arr = Array.from(arrayLike)
對象的擴展運算符
let obj1 = {
    a:1,
    b:1,
}
let obj2 = {
    ...obj1,
    c:1
}
console.log(obj2);//{a: 1, b: 1, c: 1}

//vuex中
computed: {
    ...mapGetters('raplaceBattery', {
      runningTask: 'runningMainTask'
    })
},
methods:{
    ...mapActions('raplaceBattery', [
      'getMainTaskList'
    ]),
}

四、數組和對象的遍歷方式


數組和對象的遍歷方式有不少種,經常使用的for循環,for...in循環以及ES6提出的for...of循環,這些方式有各自的使用場景以及優缺點。
數組的遍歷方式

如下遍歷的數組都爲:let arr = [1,2,3,4]

方式1:for

//寫法比較麻煩,可是經常使用
for (let i = 0; i < arr.length; i++) {
    console.log('for循環:',arr[i]);
}
//使用break跳出循環,return會報錯
for (let i = 0; i < arr.length; i++) {
    if(i === 2) break
    console.log('for循環:',arr[i]);//1,2
}

方式2:forEach

arr.forEach(ele => {
    console.log('forEach循環:',ele);
});
//缺點:沒法跳出循環,break直接會報錯,return只是跳出本次循環
arr.forEach(ele => {
    if(ele === 2) return;
    console.log('forEach循環:',ele);//1,2,4
});

方式3:map

返回新的數組,不改變原數組
let mapArr = arr.map((item,index,arr)=>{
    if(index === 2) return;//只是跳出本次循環,
    return item+1
})
console.log(arr);//[1, 2, 3, 4]
console.log(mapArr);//[2, 3, undefined, 5]

//map有一個選填的返回函數裏面的this的參數
let mapArr = arr.map(function (e) {
    return e + this; // 此處的 this爲10
}, 10);
console.log(mapArr);//[11, 12, 13, 14]

這裏爲何要用回調函數而不用箭頭函數?
答:由於箭頭函數裏的this指向和函數裏的this指向不一樣。請了解箭頭函數的this指向(後續)

方式4:filter,find,findIndex

filter:返回新數組,不改變原數組,主要用於過濾
find:返回 第一個判斷條件的 元素,不是數組
findIndex:返回 第一個判斷條件的 元素的索引
let filterArr = arr.filter((item,index,arr)=>{
    return item > 1
})
console.log(filterArr);//[2, 3, 4]

//find
let findArr = arr.find((value,index,arr)=>{
    return value > 2
})
console.log(findArr);//3

//findIndex
let findIndexEle = arr.findIndex((val,i,arr)=>{
    return val > 2
})
console.log(findIndexEle);// 2

方式5:every和some(返回布爾值)

every:只要有一個不符合,返回false,全符合,返回true
some:只要有一個符合,返回true,全不符合,返回false
let everyBool = arr.every((val, i, arr) => {
    return val < 3
})
console.log(everyBool);//false

let someBool = arr.some((val,i,arr)=>{
    return val > 2
})
console.log(someBool);//true

方式6:keys(),values(),entries()

ES6提供的新方法用於遍歷數組,都返回 遍歷器對象
let keysArr = arr.keys()
console.log(keysArr);//Array Iterator {}

//這種方式不能夠哦
arr.keys().forEach(ele => {
    console.log(ele);//Uncaught TypeError: arr.keys(...).forEach is not a function
});

//for...of能夠
for (const index of arr.keys()) {
    console.log(index);//0,1,2,3
}

for (const val of arr.values()) {
    console.log(val);//1,2,3,4
}

for (const [index,ele] of arr.entries()) {
    console.log(index,ele);
}
//0 1
//1 2
//2 3
//3 4

entries方法bmom使用次數.png

方法7:for...of循環

遍歷全部部署了iterator接口的數據結構的方法,如:數組,Set,Map,類數組對象,如 arguments 對象、DOM NodeList 對象,Generator 對象,字符串

場景1:用於set和map結構

遍歷set結構返回的是 一個值,遍歷map結構返回的是一個數組
遍歷的順序是各個成員添加的順序
let setArr = new Set([1,2,3,3])
let mapObj = new Map().set('b',1).set('a',2)
for (const item of setArr) {
    console.log(item);//1,2,3
}

for (const item of mapObj) {
    console.log(item);//item是數組,別搞錯了
}
//["b", 1]
//["a", 2]

for (const [item, index] of mapObj) {
    console.log(item, index);
}
//b 1
//a 2

場景2:類數組對象(必須部署了Iterator接口)

//字符串
const str = 'wx'
for (const item of str) {
    console.log(item);//w,x
}

//DOM NodeList對象
let ps = document.querySelectorAll('div')
for (const p of ps) {
    console.log(p);
}

//arguments對象
function writeArgs(){
    for (const item of arguments) {
        console.log(item);
    }
}
writeArgs('1',2)//1,2

//rest的方式
function writeArgs(...rest){
    for (const item of rest) {
        console.log(item);
    }
}
writeArgs('1',2)//1,2

//沒有部署的類數組對象,經過Array.from轉爲數組
let likeArr = {length:2,0:'1',1:'2'}
for (const item of Array.from(likeArr)) {
    console.log(item);//1,2
}

場景3:普通對象

普通對象不能使用for...of遍歷,由於沒有部署Iterator接口,通常使用for...in進行鍵名遍歷
let obj = {
    a:1,
    b:2,
    c:3
}
for (const item of obj) {
    console.log(item);//Uncaught TypeError: obj is not iterable
    
//你非要使用for...of呢?你調皮了

// 方式1:
for (const item of Object.keys(obj)) {
    console.log(item);//a,b,c
}

// 方式2:使用Generator函數進行包裝
function* packObj(obj){
    for (const key of Object.keys(obj)) {
        yield [key,obj[key]]
    }
}
for (const [key,value] of packObj(obj)) {
    console.log(key,value);
}
}
//a 1
//b 2
//c 3
對象的遍歷方式

如下遍歷的對象都爲:

let obj = {
    a:1,
    b:2,
    c:3,
    [Symbol('mySymbol')]: 4,
}
Object.setPrototypeOf(obj,{name:'wx'}) ////給原型加了name屬性
//Object.getOwnPropertyDescriptor(obj,'enumTemp').enumerable = false //這種方式無效
Object.defineProperty(obj, 'enumTemp', {
    value: 5,
    enumerable: false
})//添加一個不可枚舉屬性enumTemp

1. for...in

特色:遍歷對象 自身的和 繼承的可枚舉屬性(不含Symbol屬性), 不建議使用
for (const key in obj) {
    console.log(key);//a,b,c,name
}

2. Object.keys(obj),Object.entries(obj)

特色:只包括對象自身的可枚舉屬性--(不含Symbol屬性,不含繼承的), 推薦使用
Object.keys(obj).forEach(item=>{
    console.log(item);//a,b,c
})

for (const [item,index] of Object.entries(obj)) {
    console.log(item,index);
}
//a 1
//b 2
//c 3

3. Object.getOwnPropertyNames(obj)

特色:包括對象自身全部的屬性(包括不可枚舉的)--(不含Symbol屬性,不含繼承的)
console.log(Object.getOwnPropertyNames(obj));
//["a", "b", "c", "enumTemp"]

4. Object.getOwnPropertySymbols(obj)

特色:返回數組包括自身全部symbol屬性的鍵名
console.log(Object.getOwnPropertySymbols(obj));//[Symbol(mySymbol)]
let sym = Object.getOwnPropertySymbols(obj).map(item=>obj[item])
console.log(sym);//[4]

五、Proxy、Reflect和Object.defineProperty


它們都是爲了操做 對象而設計的API,而且主要用於 監聽數據變化以及 響應式能力

1. Object.defineProperty
具體更詳細的瞭解請查看:Object.defineProperty

//基本形式
let obj = {};
Object.defineProperty(obj, "num", {
   value: 1,
   writable: true,
   enumerable: true,
   configurable: true
});
console.log(obj.num) //1

//get,set形式
let obj = {};
let value = 2;
Object.defineProperty(obj, 'num', {
   get: function () {
       console.log('執行了 get 操做')
       return value
   },
   set: function (newVal) {
       console.log('執行了 set操做');
       value = newVal
   },
   enumerable: true,
   configurable: true
})
console.log(obj.num); // 執行了 get 操做 //2

2. Proxy

Tips:vue3.0使用Proxy代替Object.defineProperty實現數據響應式。

它是一個 「攔截器」,訪問一個對象以前,都必須先通過它,對比defineProperty只有get,set兩種處理攔截操做,而Proxy有13種,極大加強了處理能力,而且也消除了Object.defineProperty存在的一些侷限問題

它爲什麼優秀呢?

  • 它有13種操做攔截對象的方法,如:ownKeys(target)apply(target, object, args)defineProperty(target, propKey, propDesc)。。。
  • 消除了Object.defineProperty存在的一些侷限問題:對屬性的添加、刪除動做的監測,對數組基於下標的修改,對於 .length修改的監測,對 Map、Set、WeakMap 和 WeakSet 的支持。(尤大Vue3.0說的)
//一、基礎用法
let proxy = new Proxy({}, {
    get(target, key, receiver) {
        console.log('get 操做');
        console.log(receiver);//Proxy {name: "wx"}
        return target[key]
    },
    set(target, key, val, receiver) {
        console.log('set 操做');
        console.log(receiver);//Proxy {}
        target[key] = val
    }
})
proxy.name = 'wx' //set 操做
console.log(proxy.name); //get 操做 //wx

//二、has用法,攔截propKey in proxy的操做,返回一個布爾值
let handler = {
    has(target, key) {
        if (key[0] === '_') {
            return false
        }
        return key in target
    }
}
let target = { prop: 'foo', _prop: 'foo' };
let proxy = new Proxy(target, handler)
console.log('_prop' in proxy)//false

//三、deleteProperty方法攔截delete操做,返回布爾值
let target = { prop: 'foo', _prop: 'foo' };
let proxy = new Proxy(target, {
    deleteProperty(target, key) {
        if (key[0] === '_') {
            return false
        }
        delete target[key]
        return true
    }
})
console.log(delete proxy['_prop']) //false
console.log(target) //{prop: "foo", _prop: "foo"}//這裏並無刪除‘_prop’屬性

//四、apply方法攔截函數的調用,call和apply操做,它有三個參數,分別是:目標對象,目標對象的上下文(this)和目標對象的參數數組
let target = () => 'target function'
let proxy = new Proxy(target, {
    apply(target, ctx, args) {
        return 'apply proxy'
    }
})
console.log(proxy()) //apply proxy

3. Reflect

它和Proxy同樣,也是用來處理對象,常常與proxy搭配使用,用來保證這些方法 原生行爲的正常執行,因此它是服務於proxy的。
//一、基本用法
    let obj = {
        'a': 1,
        'b': 2
    }
    let proxy = new Proxy(obj, {
        get(target, key) {
            console.log('get', target, key);
            return Reflect.get(target, key);
        },
        deleteProperty(target, key) {
            console.log('delete' + key);
            return Reflect.deleteProperty(target, key);
        },
        has(target, key) {
            console.log('has' + key);
            return Reflect.has(target, key);
        }
    });
    console.log(proxy.b);
    //get {a: 1, b: 2} b
    //2 //原生行爲

//二、has用法
    let myObject = {
        foo: 1,
    };
    // 舊寫法
    console.log('foo' in myObject);// true

    // 新寫法
    console.log(Reflect.has(myObject, 'foo'));// true

//三、deleteProperty用法
    let myObject = {
        foo: 1,
        b:2
    };
    // 舊寫法
    console.log(delete myObject['foo'])//true
    console.log(myObject);//{b: 2}

    // 新寫法
    console.log(Reflect.deleteProperty(myObject,'b'));//true
    console.log(myObject);//{}

六、Promise 對象


Promise是解決異步編程提出的新的解決方案,它相對於以前的 回調函數方案在不少方面作了改進,更加的合理和強大。
理解Promise以前建議先了解 異步事件循環以及 回調函數
  • 回調函數

回調函數是以前用來解決異步編程經常使用的方式,大概的流程是:前端發出一個請求,進入瀏覽器的http請求線程,等收到響應後,將該回調函數推入異步隊列,等處理完主線程的任務後會逐個讀取異步隊列中的回調並開始執行。
一、第三方庫信任問題及錯誤處理

ajax('http://localhost:3000',()=>{
    console.log('執行回調');
})

上面這個例子是最多見的一個回調例子,可是它有什麼問題呢?試想一下,若是這個請求在網絡環境很差的狀況下,進行了超時重試的操做,那麼這個回調函數就會執行屢次,另一種狀況,要是這個請求失敗了,那麼它失敗的錯誤信息怎麼拿到,這些都是回調函數可能存在的問題,這明顯不是咱們但願看到的結果。

二、回調地獄

//引用《你不知道的JavaScript(中卷)》
listen("click", function handler(evt) {
    setTimeout(function request() {
        ajax("http://some.url.1", function response(text) {
            if (text == "hello") {
                handler();
            } else if (text == "world") {
                request();
            }
        });
    }, 500);
});

總結:

  1. 多重嵌套,致使回調地獄
  2. 代碼邏輯複雜以後,很難理清代碼結構
  3. 第三方庫信任問題及錯誤處理不透明
  • Promise
它是一個構造函數,用來生成promise實例對象,它有兩個參數(是函數),通常推薦命名爲:resolve和reject

特色:
(1)、它有三種狀態:pending(進行中)、fulfilled(已成功)、rejected(已失敗)
(2)、它的狀態改變只有兩種可能:pendingfulfilled或者pendingrejected
(3)、promise新建後會當即執行

// 基礎用法
let p = new Promise((resolve, reject) => {
    if (true) {
        return resolve('1') //resolve(new Promise())
        // 後面寫的代碼沒用了
    } else {
        reject('error')
    }
})
p.then((val) => {
    console.log('fulfilled:', val);
}).catch(e => { //推薦使用catch進行錯誤處理,能夠檢測到promise內部發生的錯誤
    console.log('catch', e);
}).finally(()=>{...})
//對比兩種方式
// bad
request(url, function (err, res, body) {
    if (err) handleError(err);
    fs.writeFile('1.txt', body, function (err) {
        request(url2, function (err, res, body) {
            if (err) handleError(err)
        })
    })
});

// good
request(url)
    .then(function (result) {
        return writeFileAsynv('1.txt', result)
    })
    .then(function (result) {
        return request(url2)
    })
    .catch(function (e) {
        handleError(e)
    });

1. Promise.all([...])

將多個Promise(p1,p2,p3)實例包裝成一個新的promise實例,這個新實例的狀態由p1,p2,p3共同決定。
什麼時候用它?當多個任務之間沒有必然聯繫,它們的順序並不重要,可是必須都要完成才能執行後面的操做
const p1 = request("http://some.url.1/");
const p2 = request("http://some.url.2/");

Promise.all([p1, p2]).then(([p1Result, p2Result]) => {
    // 這裏p1和p2所有響應以後
    return request("http://some.url.3/")
}).then((result) => {
    console.log(result);
}).catch(e => {
    console.log(e);
})

2. Promise.race([...])

它與Promise.all([...])相似,只是它只關心誰先第一個完成Promise協議,一旦有一個完成,它就完成。即各個promise之間(p1,p2,p3...)存在「競爭」關係
const p = Promise.race([
    request('/fetch-request'),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error('request timeout'))
        }), 5 * 1000
    })
]);

p.then(console.log)
 .catch(console.error);

上面的代碼中,5秒以內沒法從request中返回結果,p的狀態就會變爲reject,從而執行catch

3. Promise.resolve()和Promise.reject()

有時候須要將現有的對象 轉化爲promise對象進行處理,如 deferred或者 thenable對象
注意:當即resolve()的promise對象,是在「 本輪事件循環」結束時執行,而不是等到下一輪才執行
//deferred轉爲promise
const deferToPromise = Promise.resolve($.ajax('/a.json'))

//thenable轉promise
let thenable = {
    then(resolve, reject) {
        resolve(11)
    }
};
let p1 = Promise.resolve(thenable);
p1.then((val) => {
    console.log(val);//11
});

//純對象轉promise
let obj = {
    a:1,
    b:2
}
const p = Promise.resolve(obj)
p.then((v)=>{
    console.log(v);//{a: 1, b: 2}
}).finally(()=>{
    console.log('aaa');
})

七、async...await函數


它被稱爲異步編程的終極解決方案,它進一步 優化了promise的寫法,用 同步的方式書寫異步代碼,而且可以更優雅的實現異步代碼的順序執行。
它返回一個promise對象。
任何一個 await語句後面的 Promise 對象變爲 reject狀態,那麼整個 async函數都會中斷執行嗎?。
// 基本用法
async function request(){
    const data = await requestApi()
    // const data1 = await requestApiOther()
    return data
}
request().then((val)=>{
    console.log(val);
}).catch((e)=>{
    console.log(e);
})

重點關注:

async function f() {
    await Promise.reject('出錯了');
    await Promise.resolve('hello world'); 
}

上面的這種狀況,後面的await不會執行,可是你就想讓後面的也執行呢?

async function f() {
    // 方式:1:
    try {
        await Promise.reject('出錯了');

    } catch (e) {
        console.log(e)
    }
    // 方式2:
    // await Promise.reject('出錯了').catch(e => {
    //     console.log(e);

    // })
    return await Promise.resolve('hello world');
}
f().then((v) => {
    console.log(v);

}).catch(e => {
    console.log(e);

})

推薦使用:
一、將await使用try...catch包裹

async function f(){
    try {
        await someRequest()
    } catch (error) {
        console.log(error);
    }
}

二、多個await後面的異步操做若是不存在依賴關係,最好同時觸發

async function f(){
    try {
        await Promise.all([getFoo(), getBar()]);
    } catch (error) {
    }
}
f().then(([foo,bar])=>{
// handle
}).catch(e=>{
    console.log(e);

})

三、多個await後面的異步操做若是存在依賴關係,請參照如下寫法

function fetchData() {
    return Promise.resolve('1')
  }

function fetchMoreData(val) {
    return new Promise((resolve, reject) => {
      resolve(val * 2)
    })
  }

function fetchMoreData2(val) {
    return new Promise((resolve, reject) => {
      resolve(val * 3)
    })
  }

// good
  function fetch() {
    return fetchData()
      .then(val1 => {
        return fetchMoreData(val1)
      })
      .then(val2 => {
        return fetchMoreData2(val2)
      })
  }
  fetch().then(val => {
    console.log(val) //6
  })

// better
async function fetch() {
    const value1 = await fetchData()
    const value2 = await fetchMoreData(value1)
    return fetchMoreData2(value2)
};

建議在項目中多使用ES7的async...await寫法,它簡潔,優雅,可維護性高。

未完待續。。。

參考資料連接:

  1. ES6 入門教程--阮一峯
  2. 近一萬字的ES6語法知識點補充
  3. 一文快速掌握 es6+新特性及核心語法
  4. ES6 徹底使用手冊
  5. ECMAScript 201六、2017和2018中全部新特性
相關文章
相關標籤/搜索