閱讀66 返回首頁    go 阿裏雲 go 技術社區[雲棲]


三十分鍾熟悉es6(es6常用功能總結)

1、前注

關於es6的詳細說明,可以參照我的係列文章es6 從入門到熟練,或者阮一峰的ECMAScript 6 入門

我的係列文章,是在阮一峰的基礎上,增加了更多適合初中級開發者的內容(包括大量的示例代碼和解釋),以降低學習難度,豐富說明。

本文是對es6整體的回顧,結合我的實際開發經驗,對es6的一個小結。

為了精煉內容,es6裏不常用的內容已經去掉,而對常用、重要的es6知識,附上簡單的代碼說明,並另附有詳細說明的博文鏈接,方便初中級開發者理解。

2、開發環境

關鍵字:IE9、Babel、Babel的墊片、腳手架

首先,使用es6的前提是最低IE9,如果你需要兼容IE8,建議放棄es6,專心使用神器jQuery。

其次,如果需要使用es6來編寫,那麼你需要Babel轉碼器用於將你的es6代碼轉換為es5代碼,用於兼容隻能使用es5的環境。否則對於隻能運行es5的環境(例如IE9),是無法運行es6代碼的。

第三,由於Babel在默認情況下,並不是全部轉換的,如以下說明:

Babel 默認隻轉換新的 JavaScript 句法(syntax),而不轉換新的 API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局對象,以及一些定義在全局對象上的方法(比如Object.assign)都不會轉碼。

因此,我們需要墊片,一般情況下可以用babel-polyfill,也可以用babel-runtime,這兩個有所差異。

babel-polyfill會汙染全局對象,即會對例如Object這樣的對象添加方法,而babel-runtime隻會轉換es6語法的代碼,但如果你需要調用Object.assign這樣的方法,那就不行了。

由於細節很多,因此這裏給幾個參考鏈接吧:

Babel 全家桶

babel的polyfill和runtime的區別

看完以上兩個,可能會覺得應該同時使用這兩個,然而並不需要,看下麵這個鏈接:

transform-runtime 會自動應用 polyfill,即便沒有使用 babel-polyfillconanliu17 Dec 2016提交的issues。

如果你使用的Vue.js,那麼可以直接fork我的腳手架,然後當做自己的腳手架使用。

附腳手架鏈接:vue-scaffold,如果可以,給個star喔~~

如果你用的不是Vue.js,那麼可以去搜一下你所使用的框架的腳手架,然後拿來使用。如果找不到,可以找使用帶腳手架的該框架項目,然後down下來,刪除對方的項目隻取殼來用即可。(如果有許可,記得閱讀一下許可看能不能這麼幹)

3、let和const

既然有let和const了,那麼推薦優先使用這兩個。

一般情況下,let可以直接替代var,對於常量,可以用const。

這不是必須的,但用這2個可以幫你規範寫代碼的習慣,所以還是強烈推薦的。

比較蛋疼的是,用webtorm,let有時候不會高亮,隻有var和const是高亮的。這個可能是用的風格的問題,我也不太確定。解決方案我自己是沒找到,湊合用吧。

另外,let和var之間的一個重要區別是變量提升,所以如果你寫代碼不太規範的話,可能會報錯,好好檢查一下吧。

另外,阮一峰推薦將**函數設置為常量**,就像這樣子:

const add = function (a, b) {
    return a + b
}

我覺得挺有道理的,推薦。

4、字符串

既然用es6,當然要用反引號這個高大上的東西了。

詳細用法推薦我自己的博客:ECMAScript 6(7)模板字符串

最基本的用法,可以直接用反引號替代普通的引號(單引號和雙引號)

例如:

let a = 'ab'
// 可以直接用以下替換
let a = `ab`

而且一般情況下,簡單需求不用再拚接字符串了~(另外,反引號也可以像普通字符串那樣拚接)

如:

let str = '20004604';
let html = 'my QQ is ' + str;

//用以下替換
let str = '20004604';
let html = `my QQ is ${str}`;

簡單暴力省事。

5、解構賦值

最大的好處是簡化了寫法,如代碼:

let obj = {
    a: 1,
    b: 2
}

//old
let a = obj.a;
let b = obj.b;

// es6
let {a, b} = obj

除了對象之外,還有數組也可以解構賦值,別忘了。

6、對象

es6的對象,比早期版本的寫起來舒服很多。

例如:

  1. 對象屬性是函數的時候可以簡寫;
  2. setter和getter的簡寫;
  3. 通過Object.assign()來合並對象,實現繼承或添加屬性效果;
  4. 可以用屬性名表達式;
  5. 可以用變量名隻要作為對象的屬性名,並且變量的值可以自動成為對象該屬性名的值;

列一些常見寫法:

let obj = {
    // 對象屬性是函數的時候可以簡寫
    a(){
        console.log('對象屬性是函數的時候可以簡寫')
    },
    // setter和getter的簡寫;
    get b() {
        return this._b
    },
    set b(val) {
        this._b = val
    }
}


let c = '添加了一個c'
// 通過``Object.assign()``來合並對象,實現繼承或添加屬性效果
// 可以用變量名隻要作為對象的屬性名,並且變量的值可以自動成為對象該屬性名的值
Object.assign(obj, {
    c
})

// 可以用屬性名表達式
let d = "abcd"
obj[d.replace(/abc/, '')] = '屬性名表達式'

7、數組

最常用的就兩個:

  1. 擴展運算符...
  2. 將類數組的轉為數組的Array.from()

如代碼:

function getArgs() {
    let foo = [...arguments]
    console.log(foo)
    let bar = Array.from(arguments)
    console.log(bar)
}
getArgs(1, 2, 3)
// [1, 2, 3]
// [1, 2, 3]

需要注意的一個特性:

  1. es5在麵對,通過Array(5)這樣生成帶空位的數組時,處理他的時候會跳過空位數組的空位
  2. es6在同樣情況下,因為使用遍曆器接口,所以會進行處理(視為undefined),而不是跳過;

8、函數

函數常用特性有以下幾個:

  1. 箭頭函數:特點是this永遠指向聲明時的父級作用域,寫起來比普通函數簡單;
  2. bind:可以給函數綁定this,並將這個綁定後的函數返回(不影響原函數);
  3. rest函數:即函數參數使用例如function test(..args){}這樣的,這個返回的是一個數組,而不是類數組。
  4. 參數默認值:一般帶默認值的參數,放在參數列表的後麵。
function test(a, b = 3) {
    console.log(a, b)
    console.log(this)
}
test.bind('Is this')(1)
// 1 3
// Is this

function test2(...args) {
    console.log(args.length)
}
test2(1, 2, 3, 4, 5)
// 5

9、Set和Map

Set結構最大的特點是去重,Map結構最大的特點是kv結構。

Set:

Set和數組類似,可以存儲元素,但是Set不能存儲相同的值。

非引用類型變量來說,就是值相等;對於引用類型變量來說,指地址相等(而不是值相等)。詳細情況請點擊Set類型和WeakSet查看。

至於去重,一般是對數組使用。先作為參數生成一個Set類型變量,再利用擴展運算符變回數組,去重完成,完美。

利用擴展運算符,調用Set的迭代器接口

// 去重
let foo = new Set([1, 2, 3, 3, 3])
console.log([...foo]);  // [1, 2, 3]

Map:

Map結構和對象非常類似,不過最大的區別在於,Map結構可以用其他類型作為key,例如數組、對象等。

Map可以參照這篇博客Map和WeakMap

示例代碼:

let zhang = {
    firstName: "王"
}
let property = {
    gender: "男"
}
let foo = new Map()
foo.set(zhang, property)
foo.has(zhang)  // true
foo.get(zhang)  // {gender: "男"}

10、Promise

Promise是es6的精華之一,他非常適用於異步處理。

Promise對象在使用的時候,分為兩部分,第一部分是new Promise這一步,第二部分是對返回的Promise實例進行處理的內容。

因為是通過執行resolvereject來改變Promise的狀態,從而決定執行then的時機的(類似回調函數),以及執行的哪一個。因此寫起來和回調函數相近,但是可以連寫,避免回調地獄的情況。

關於Promise的詳細介紹請閱讀Promise(1)基礎知識及之後三篇博客

如示例代碼(對比普通ajax和promise)(另注:為了方便理解,仿jQuery的寫法,並且沒有用jQuery的$.ajax().then()這種寫法)

// 模擬ajax
function ajax (options) {
    setTimeout(function () {
        options.success(options.url)
    }, 1000)
}

// old
let foo = function (callback) {
    ajax({
        url: "/1",
        success: function (result) {
            callback(result)
        }
    })
}
let foo2 = function (result) {
    console.log(result)
    return function (callback) {
        ajax({
            url: "/2",
            success: function (val) {
                callback(val)
            }
        })
    }
}
// 核心,調用的時候如果是連續請求的話,基本要寫成回調地獄了
foo(function (result) {
    foo2(result)(function (val) {
        console.log(val)
    })
})

// Promise
let bar = function () {
    return new Promise((resolve, reject) => {
        ajax({
            url: "/1",
            success: function (result) {
                resolve(result)
            }
        })
    })
}
let bar2 = function (result) {
    console.log(result)
    return new Promise((resolve, reject) => {
        ajax({
            url: "/2",
            success: function (val) {
                resolve(val)
            }
        })
    })
}
// 核心,then連寫即可
bar().then(function (result) {
    return bar2(result)
}).then(function (result) {
    console.log(result)
})

顯然,then連寫比回調函數的寫法要方便一些。

如果麵對的是特殊需求,比如是多個ajax請求全部完成後,再執行執行函數,那麼Promise的優勢會更大一些,而非Promise寫法要麻煩很多。

甚至如果要對錯誤進行處理,那麼Promise寫法會更方便。

不過這裏隻是小結,就不細說了。

11、class

class是好東西。

有了class後,寫構造函數、寫類的繼承的難度,下降了很多很多。

先附我的博文class(1)基本概念,以及之後5篇博文。

由於很簡單,給一個示例大約就能理解這個是怎麼用的:

class Foo {
    constructor () {
        console.log('this is constructor')
        this.defaultValue = '變量要在構造函數裏賦值,而不能直接聲明'
    }

    log () {
        console.log('log')
    }
}

let foo = new Foo() // this is constructor
foo.log()   // log
foo.defaultValue   // "變量要在構造函數裏賦值,而不能直接聲明"

12、es6模塊

es6的模塊不同於以往的CommonJS(node用,服務器環境),AMD(RequireJS的規範,瀏覽器環境,依賴前置)、CMD(SeaJS定義的規範,瀏覽器環境,依賴就近)。

他的特點有兩個:

  1. 編譯時加載,因此可以做靜態優化;
  2. 模塊的引用進來的,都是值的引用,而非值的拷貝。

缺點是:

  1. 瀏覽器環境下不支持,node環境下支持的也比較差;
  2. 必須考babel轉碼後才可以正常使用,因此對某些符合規範的特性支持的不是很好;

詳細說明閱讀這篇博客:es6的import和export,另外三個規範閱讀這篇博客AMD、CMD、CommonJS

基本使用方式如示例代碼:

// foo.js
let foo = 'foo'
export default foo

// bar.js
import foo from 'foo'
console.log(foo)

13、async函數

這個並不是es6的,而是es2017(又稱es8)的內容。

可以認為async函數是Generator函數的語法糖,詳細說明參照這篇博客:async函數

他的前置知識比較多,包括Iterator遍曆器、Generator狀態機、Thunk函數(自動執行Generator 函數)。

簡單的說,假如有多個異步請求,你需要讓這些起步請求依次執行,例如在執行完前一個之後,再執行後一個。那麼你就需要async函數了。

async函數可以讓你寫這種請求如同寫同步函數一樣簡單(對比【10】中的Promise更簡單)。

以下示例是基於【10】中的代碼,在最後一步執行的時候,改用async函數來完成

// 模擬ajax
function ajax (options) {
    setTimeout(function () {
        options.success(options.url)
    }, 1000)
}

// Promise
let bar = function () {
    return new Promise((resolve, reject) => {
        ajax({
            url: "/1",
            success: function (result) {
                resolve(result)
            }
        })
    })
}
let bar2 = function (result) {
    console.log(result)
    return new Promise((resolve, reject) => {
        ajax({
            url: "/2",
            success: function (val) {
                resolve(val)
            }
        })
    })
}

async function foo () {
    let result1 = await bar()
    let result2 = await bar2(result1)
    return result2
}

foo().then(result => {
    console.log(result)
})

可以發現,async讓連續異步調用像寫同步函數一樣簡單。

14、ESLint

規範化開發,建議還是用ESLint來幫忙檢查吧。不會這個怎麼行?

ESLint是一個語法規則和代碼風格的檢查工具,可以用來保證寫出語法正確、風格統一的代碼。

這裏直接給一個阮一峰寫的文章,等以後我再單獨補一篇詳細用法的博客。

ESLint的使用

15、小結

es6常用內容基本就以上13點。

雖然es6實際包括了很多知識,例如:

  1. string方麵增加了對utf16字符的更好的支持;
  2. number方麵增加了對最大值、最小值、以及合理誤差誤差值的處理等;
  3. Symbol產生唯一變量;
  4. Proxy的代理;
  5. 遍曆器,狀態機等等。

但實際常用的就以上這些,如果隻是日常使用的話,熟悉以上內容足夠了。

但若要做的更好,那麼應該深入學習,es6新增的很多內容,是將傳統後端語言的一些很好的思想,搬到JavaScript來,讓js規範化。

對於專精於前端的同學,學習es6的過程中,可以學習到這些來自於其他語言的精華,因此建議至少完整的看一遍,勿要隻滿足於常用的這些API。

最後更新:2017-09-18 21:33:05

  上一篇:go  雲生態下的鏡像管理利器Packer
  下一篇:go  curl+tcpdump抓包腳本一例