談?wù)凟S6語法(匯總下篇)

本次的ES6語法的匯總總共分為上、中、下三篇,本篇文章為下篇。

往期系列文章:

客套話不多說了,直奔下篇的內(nèi)容~

async函數(shù)

ES2017標準引入了async函數(shù),使得異步操作更加方便。async函數(shù)是Generator函數(shù)的語法糖。不打算寫Generator函數(shù),感興趣的話可以看文檔。與Generator返回值(Iterator對象)不同,async返回的是一個Promise對象。

用法

async函數(shù)返回一個Promise對象,可以使用then方法添加回調(diào)函數(shù)。當函數(shù)執(zhí)行的時候,一旦遇到await就會先返回,等到異步操作完成,再接著執(zhí)行函數(shù)體內(nèi)后面的語句。

async function getStockPriceByName(name) {
    const symbol = await getStockSymbol(name);
    const stockPrice = await getStockPrice(symbol);
    return stockPrice;
}
getStockPriceByName('goog').then(function(result) {
    console.log(result);
})

再來看幾種情況加深下印象:

function fun1() {
  console.log('fun1');
  return 'fun1 result';
}
async function test() {
  const result1 = await fun1();
  console.log(result1);
  console.log('end');
}
test();
// 輸出 
// 'fun1'
// 'fun1 result'
// 'end'
async function fun2() {
  console.log('fun2');
  return 'fun2 result';
}
async function test() {
  const result2 = await fun2();
  console.log(result2);
  console.log('end');
}
test();
// 輸出
// 'fun2'
// 'fun2 result'
// 'end'

正常情況下,await命令后面是一個Promise對象,返回該對象的結(jié)果。如果不是Promise對象,就直接返回對應(yīng)的值。

async function fun3() {
  console.log('fun3');
  setTimeout(function() {
    console.log('fun3 async');
    return 'fun3 result';
  }, 1000)
}
async function test() {
  const result3 = await fun3();
  console.log(result3);
  console.log('end');
}
test();
// 輸出
// 'fun3'
// undefined
// 'end'
// 'fun3 async'
async function fun4() {
  console.log('fun4');
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('fun4 async');
      resolve('fun4 result');
    }, 1000);
  })
}
async function test() {
  console.log(result4);
  console.log('fun4 sync');
  console.log('end');
}
test();
// 輸出
// 'fun4'
// 'fun4 async'
// 'fun4 result'
// 'fun4 sync'
// 'end'

模擬sleep

JavaScript一直沒有休眠的語法,但是借助await命令就可以讓程序停頓指定的時間?!綼wait要配合async來實現(xiàn)】

function sleep(interval) {
    return new Promise(resolve => {
        setTimeout(resolve, interval);
    })
}
// use
async function one2FiveInAsync() {
    for(let i = 1; i <= 5; i++) {
        console.log(i);
        await sleep(1000);
    }
}
one2FiveInAsync();
// 1, 2, 3, 4, 5 每隔一秒輸出數(shù)字

一道題

需求:使用async await改寫下面的代碼,使得輸出的期望結(jié)果是每隔一秒輸出0, 1, 2, 3, 4, 5,其中i < 5條件不能變。

for(var i = 0 ; i < 5; i++){
    setTimeout(function(){
        console.log(i);
    },1000)
}
console.log(i);

之前我們講過了用promise的方式實現(xiàn),這次我們用async await方式來實現(xiàn):

const sleep = (time) => new Promise((resolve) => {
    setTimeout(resolve, time);
});

(async () => {
    for(var i = 0; i < 5; i++){
        console.log(i);
        await sleep(1000);
    }
    console.log(i);
})();
// 符合條件的輸出 0, 1, 2, 3, 4, 5

比較promise和async

為什么只比較promiseasync呢?因為這兩個用得頻繁,實在的才是需要的,而且async語法generator的語法糖,generator的說法直接戳async與其他異步處理方法的比較

兩者上,async語法寫法上代碼量少,錯誤處理能力佳,而且更有邏輯語義化。

假定某個 DOM 元素上面,部署了一系列的動畫,前一個動畫結(jié)束,才能開始后一個。如果當中有一個動畫出錯,就不再往下執(zhí)行,返回上一個成功執(zhí)行的動畫的返回值。

// promise
function chainAnimationsPromise(elem, animations) {

  // 變量ret用來保存上一個動畫的返回值
  let ret = null;

  // 新建一個空的Promise
  let p = Promise.resolve();

  // 使用then方法,添加所有動畫
  for(let anim of animations) {
    p = p.then(function(val) {
      ret = val;
      return anim(elem);
    });
  }

  // 返回一個部署了錯誤捕捉機制的Promise
  return p.catch(function(e) {
    /* 忽略錯誤,繼續(xù)執(zhí)行 */
  }).then(function() {
    return ret;
  });

}
// async await
async function chainAnimationsAsync(elem, animations) {
  let ret = null;
  try {
    for(let anim of animations) {
      ret = await anim(elem);
    }
  } catch(e) {
    /* 忽略錯誤,繼續(xù)執(zhí)行 */
  }
  return ret;
}

類class

ES6之前,是使用構(gòu)造函數(shù)來模擬類的,現(xiàn)在有了關(guān)鍵字class了,甚是開心??

function Person() {}
Person.prototype.sayHello = function(){
    console.log('Hi');
};
class Person{
    sayHello(){
        console.log('Hi!');
    }
}

constructor方法

constructor方法是類的默認方法,通過new命令生成對象實例時,自動調(diào)用該方法,一個類中必須有construtor方法,如果沒有顯式定義,一個空的constructor方法會默認添加。

class Person{}
// 等同于
class Person{
    constructor(){}
}

construtor方法也就類似構(gòu)造函數(shù),在執(zhí)行new的時候,先跑構(gòu)造函數(shù),再跑到原型對象上。

取值函數(shù)(getter)和存值函數(shù)(setter)

與ES5一樣,在的內(nèi)部可以使用getset關(guān)鍵字,對某個屬性設(shè)置存值函數(shù)和取值函數(shù),攔截該屬性的存取行為。

class MyClass {
    get prop() {
        return 'getter';
    }
    set prop(value) {
        console.log(`setter: ${ value }`)
    }
}

let inst = new MyClass();

inst.prop = 123;
// 'setter: 123'

console.log(inst.prop);
// 'getter'

this的指向

類的方法內(nèi)部如果含有this,它默認是指向類的實例。但是,必須非常小心,一旦單獨使用該方法,很可能報錯。

class Person{
    constructor(job) {
        this.job = job;
    }
    printJob() {
        console.log(`My job is ${ this.job }`);
    }
  sayHi() {
    console.log(`I love my job -- ${ this.job }.`)
  }
}
const person = new Person('teacher');
person.printJob(); // 'My job is teacher'
const { sayHi } = person;
sayHi(); // 報錯: Uncaught TypeError: Cannot read property 'job' of undefined

上面的代碼中,sayHi方法單獨使用,this會指向該方法運行時所在的環(huán)境(由于class內(nèi)部是嚴格模式,所以this實際上指向undefined)。

修正上面的錯誤也很簡單,也是我們在react開發(fā)中經(jīng)常使用的一種手段:在調(diào)用構(gòu)造函數(shù)實例化的時候直接綁定實例(this),修改如下:

class Person{
    constructor(job) {
        this.job = job;
        this.sayHi = this.sayHi.bind(this);
    }
}

繼承

ES5中繼承的方式我之前有整理過--JavaScript 中的六種繼承方式

ES6中的繼承通過extends關(guān)鍵字實現(xiàn),比ES5的實現(xiàn)繼承更加清晰和方便了。

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

class ColorPoint extends Point {
  constructor(x, y, color) {
    this.color = color;
  }
}

let cp = new ColorPoint(25, 8, 'green'); // 報錯: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

上面這樣寫,不能繼承構(gòu)造函數(shù)里面的屬性值和方法。需要在子類的構(gòu)造函數(shù)中加上super關(guān)鍵字。改成下面這樣即可:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 調(diào)用父類的construtor(x, y),相當于ES5中的call。注意的是,super要放在子類構(gòu)造函數(shù)的第一行
    this.color = color;
  }
}

let cp = new ColorPoint(25, 8, 'green');

module模塊

在ES6之前,社區(qū)制定了一些模塊的加載的方案,最主要的有CommonJSAMD兩種。前者用于服務(wù)器,后者用于瀏覽器。

// CommonJS
let { stat, exists, readFile } = require('fs');

ES6在語言標準的層面上,實現(xiàn)了模塊功能,而且實現(xiàn)得相當簡單,完全可以取代 CommonJSAMD規(guī)范,成為瀏覽器和服務(wù)器通用的模塊解決方案。

// ES6模塊
import { stat, exists, readFile } from 'fs';

各種好處詳細見文檔

export命令

export命令用于規(guī)定模塊的對外接口 。

一個模塊就是一個獨立的文件。該文件內(nèi)部的所有變量,外部無法獲取。你可以理解為一個命名空間~

想要獲取模塊里面的變量,你就需要導(dǎo)出export

// profile.js
const name = 'jia ming';
const sayHi = function() {
    console.log('Hi!');
}

export { name, sayHi };

還有一個export default命令,方便用戶(開發(fā)者啦)不用閱讀文檔就能加載模塊(實際上就是輸出一個default變量,而這個變量在import的時候是可以更改的):

// export-default.js
export default function () {
  console.log('foo');
}

其他模塊加載該模塊時,import命令可以為該匿名函數(shù)指定任意名字。

// import-default.js
import customName from './export-default';
customName(); // 'foo'

import命令

import命令用于輸入其他模塊提供的功能。使用export命令定義了模塊的對外接口以后,其他JS文件就可以通過import命令加載這個模塊。

// main.js
import { name, sayHi } from './profile.js';

function printName() {
    console.log('My name is ' + name);
}

至此,本系列文章談?wù)凟S6語法已經(jīng)寫完,希望文章對讀者有點點幫助。本系列的內(nèi)容是個人覺得在開發(fā)中比較重要的知識點,如果要詳細內(nèi)容的話,請上相關(guān)的文檔查看~??

參考和后話

本次的ES6語法的匯總總共分為上、中、下三篇,本篇文章為下篇。

系列文章至此已經(jīng)完結(jié)!

文章首發(fā)在github上--談?wù)凟S6語法(匯總下篇)。更多的內(nèi)容,請戳我的博客進行了解,能留個star就更好了??

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 含義 async函數(shù)是Generator函數(shù)的語法糖,它使得異步操作變得更加方便。 寫成async函數(shù),就是下面這...
    oWSQo閱讀 2,043評論 0 2
  • 本文為阮一峰大神的《ECMAScript 6 入門》的個人版提純! babel babel負責將JS高級語法轉(zhuǎn)義,...
    Devildi已被占用閱讀 2,131評論 0 4
  • 以下內(nèi)容是我在學(xué)習(xí)和研究ES6時,對ES6的特性、重點和注意事項的提取、精練和總結(jié),可以做為ES6特性的字典;在本...
    科研者閱讀 3,278評論 2 9
  • async 函數(shù) 含義 ES2017 標準引入了 async 函數(shù),使得異步操作變得更加方便。 async 函數(shù)是...
    huilegezai閱讀 1,315評論 0 6
  • 七、Iterator 迭代器是一種接口、是一種機制。為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問機制。任何數(shù)據(jù)結(jié)構(gòu)只要部署 ...
    追逐_e6cf閱讀 496評論 0 5

友情鏈接更多精彩內(nèi)容