序
ES6,或許應該叫 ES2015(2015 年 6 月正式釋出),對於大多數前端同學都不陌生。
首先這篇文章不是工具書,不會去過多談概念,而是想聊聊關於每個特性 你可能不知道的事,希望能為各位同學 正確使用 ES6,提供一些指導。
對於 ES6,有些同學已經在專案中有過深入使用了,有些則剛剛開始認識他,但不論你是屬於哪一類,相信這篇文章都有適合你的部分。針對文章中的問題或不同意見,歡迎隨時拍磚、指正。
正文
Let + Const
這個大概是開始瞭解 ES6 後,我們第一個感覺自己完全明白並興致勃勃的開始使用的特性。
以如下方式使用的同學請舉下手?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 定義常量 const REG_GET_INPUT = /^\d{1,3}$/; // 定義配置項 let config = { isDev : false, pubDir: './admin/' } // 引入 gulp let gulp = require('gulp'); // 引入gulp相關外掛 let concat = require('gulp-concat'); let uglify = require('gulp-uglify'); let cssnano = require('gulp-cssnano'); |
很多人看完概念之後,第一印象都是:“const
是表示不可變的值,而 let
則是用來替換原來的 var
的。”
所以就會出現上面程式碼中的樣子;一段程式碼中出現大量的 let
,只有部分常量用 const
去做定義,這樣的使用方式是錯誤的。
你可能不知道的事
const
的定義是不可重新賦值的值,與不可變的值(immutable value)不同;const
定義的 Object,在定義之後仍可以修改其屬性。
所以其實他的使用場景很廣,包括常量、配置項以及引用的元件、定義的 “大部分” 中間變數等,都應該以const
做定義。反之就 let
而言,他的使用場景應該是相對較少的,我們只會在 loop(for,while 迴圈)及少量必須重定義的變數上用到他。
猜想:就執行效率而言,
const
由於不可以重新賦值的特性,所以可以做更多語法靜態分析方面的優化,從而有更高的執行效率。
所以上面程式碼中,所有使用 let
的部分,其實都應該是用 const
的。
Template Strings(字串模板)
字串模板是我剛接觸ES6時最喜歡的特性之一,他語法簡潔,語義明確,而且很好的解決了之前字串拼接麻煩的問題。
因為他並不是 “必須” 的,而且原有的字串拼接思維根深蒂固,導致我們很容易忽視掉他。
使用例項
我們先來看看他的一般使用場景:
1 2 3 4 5 6 7 8 9 10 11 12 |
const start = 'hi all'; const getName = () => { return 'jelly'; }; const conf = { fav: 'Coding' }; // 模板 const msg = `${start}, my name is ${getName()}, ${conf.fav} is my favourite`; |
你可能不知道的事
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 1. 與引號混用 const wantToSay = `I'm a "tbfed"`; // 2. 支援多行文字 const slogan = ` I have a dream today! `; // 比較適合寫HTML const resultTpl = ` <section> <div>...</div> </section> `; |
Enhanced Object Literals(增強的物件字面量)
增強的物件字面量是 ES6 中的昇華功能,他設計了很多簡寫,這些簡寫不但保留了明確的語義,還減少了我們多餘的程式碼量。
當他的使用成為一個習慣時,我們會看到自己程式碼變得更為優雅。
你可能不知道的事
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
const _bookNum = 4; const basicConfig = { level: 5 } const config = { // 直接指定原型物件 __proto__: basicConfig, // 屬性簡寫 _bookNum, // 方法簡寫 getBookNum() { return this.bookNum; } } |
Arrows and Lexical This(箭頭函式)
箭頭函式是ES6中的一個新的語法特性,他的用法簡單,形態優雅,備受人們青睞。
大多數同學初識這個特性時,更多的僅僅用它作為函式定義的簡寫,這其實就有些屈才了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
// 未使用箭頭函式的寫法 { ... addOptions: function (options) { var self = this; options.forEach(function(name, opts){ self[name] = self.addChild(name, opts); }); } } // 使用箭頭函式後的寫法 { ... addOptions: function (options) { options.forEach((name, opts) => { this[name] = this.addChild(name, opts); }); } } |
可以注意到上下兩段程式碼的區別。
在未使用箭頭函式前,我們在過程函式中使用父級 this
,需要將其顯式快取到另一箇中間變數中,因為過程函式有獨立的 this
變數,會覆蓋父級;使用箭頭函式後,不但簡寫了一個過程函式( forEach
的引數),還省略掉了 this
的中間變數的定義。
原因:箭頭函式沒有獨立執行上下文( this
),所以其內部引用 this
物件會直接訪問父級。
插播:原來我們定義這個中間變數還有一個有趣的現象,就是明明千奇百怪,例如
self, that, me, _that, _me, Self...
,快站出來說說你用過哪個,還是哪幾個~
當然,從這塊我們也可以看出,箭頭函式是無法替代全部 function
的使用場景的,例如我們需要有獨立 this
的函式。
你可能不知道的事
- 箭頭函式不但沒有獨立
this
,他也沒有獨立的arguments
,所以如果需要取不定參的時候,要麼使用function
,要麼用 ES6 的另一個新特性 rest(具體在 rest 中會有詳解)。 - 箭頭函式語法很靈活,在只有一個引數或者只有一句表示式做方法體時,可以省略相應括號。
123456789101112131415// 完整寫法const getOptions = (name, key) => {...}// 省略引數括號const getOptions = key => {...}// 省略引數和方法體括號const getOptions = key => console.log(key);// 無引數或方法體,括號不能省略const noop = () => {};
有個簡單小栗子,這一靈活的語法在寫連續的Promise鏈式呼叫時,可以使程式碼更加優雅
1 2 3 4 5 6 7 8 9 10 11 |
gitPromise .then(() => git.add()) .then(() => git.commit()) .then(() => git.log()) .then((msg) => { ... }) .then(() => git.push()) .catch((err) => { utils.error(err); }); |
Destructuring(解構)
解構這個特性可以簡單解讀為分別定義,用於一次定義多個變數,常常用於分解方法返回物件為多個變數,分別使用。
使用過ES6的同學應該或多或少接觸過這個特性,但是你可能不知道它如下幾個用法:
你可能不知道的事
1 2 3 4 5 6 7 8 9 10 11 12 13 |
const bookSet = ['UED', 'TB fed', 'Not find']; const bookCollection = () => { return {book1: 'UED', book2: 'TB fed'}; }; // 1. 解構也可以設定預設值 const {book1, book3 = 'Not find'} = bookCollection(); // 2. 解構陣列時候是可以跳過其中某幾項的 const [book1,,book3] = bookSet; // book1 = 'UED', book3 = 'Not find' // 3. 解構可以取到指定物件的任何屬性,包括它包含的方法 const {length: setLength} = bookSet; // setLength = 3 |
Rest + Spread
Rest 和 Spread 主要是應用 ...
運算子,完成值的聚合和分解。
你可能不知道的事
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 1. rest 得到的是一個真正的陣列而不是一個偽陣列 const getOptions = function(...args){ console.log(args.join); // function }; // 2. rest 可以配合箭頭函式使用,達到取得所有引數的目的 const getOptions = (...args) => { console.log(args); // array }; // 3. spread 可以用於解構時,聚合所得的值 const [opt1, ...opts] = ['one', 'two', 'three', 'four']; // 4. spread 可以用於陣列定義 const opts = ['one', 'two', 'three', 'four']; const config = ['other', ...opts]; |
Classes
ES6 中實現的一個語法糖,用於簡化基於原型整合實現類定義的場景。
雖然有很多人不太喜歡這個特性,認為它作為一個簡單增強擴充套件,並沒有其他語言 class 應有的特點。
但是就我自己觀點來看,還是感覺這樣一種寫法確實比原有的原型繼承的寫法語義更清晰、明確,而且語法更簡單。
同樣,可能有些用法是你之前容易忽略掉的,在此做個補充。
你可能不知道的事
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// 1. 靜態變數 // ES6 的類定義實現了靜態方法的定義,但靜態變數呢? // 可以用如下方式實現: class TbFedMembers{ static get HuaChen(){ return 'jelly'; } } TbFedMembers.HuaChen; // "化辰" // 2. 私有屬性(私有屬性有多種實現方式,只談及其中一種) // 閉包 const TbFedMembers = (() => { const HuaChen = 'jelly'; return class{ getOneMemberName(){ return HuaChen; } }; })(); |
Promises
Promise 不只是一個物件、一個語法,他更是一種非同步程式設計方式的變化
相信使用過 ES6 的同學都已經開始嘗試了 Promise,甚至在不支援ES6的時候,已經開始使用一些基於 Promise 思想的開源框架。
那麼我們之前用 Promise 究竟用的對麼?有什麼需要注意的點呢?
你可能不知道的事
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// 1. 多個非同步任務同時執行用 Promise.all,順序執行使用鏈式呼叫 // Promise.all Promise .all([jsBuildPromise, cssBuildPromise]) .then(() => { ... }); // chain jsBuildPromise .then(() => cssBuildPromise) .then(() => { ... }); // 2. Promise 的鏈式呼叫需要每一個過程返回一個 Promise 物件才能保證順序執行 gitPromise .then(() => git.add()) // 正確,箭頭函式簡寫 .then(() => { git.commit(); // 錯誤,函式返回 undefined,會立即執行下一過程 }) .then(() => { return git.log(); // 正確 }); // 3. Promise 需要呼叫 catch 方法來捕獲錯誤,而且過程內的錯誤不會阻塞後續程式碼執行 new Promise(() => { f; // not define error ! }) .catch((err) => { console.log(err) // show 'f is not define' }); console.log('error test'); // 此行可以被正常執行 |
結語
基礎篇主要是講了我們最常用的一些特性,後續如果大家感興趣,還可以再來個 “進階篇”,最後,希望文章中的部分內容可以對大家理解和使用 ES6 有所幫助。
參考資料
- https://www.stackoverflow.com
- https://developer.mozilla.org/en-US/docs/Web/JavaScript
- https://babeljs.io/docs/learn-es2015/
- https://developer.mozilla.org/en-US/docs/Web/JavaScript
- https://ponyfoo.com/articles/es6-spread-and-butter-in-depth
- http://12devs.co.uk/articles/promises-an-alternative-way-to-approach-asynchronous-javascript/
- http://www.2ality.com/2015/01/es6-destructuring.html
- http://www.datchley.name/es6-rest-spread-defaults-and-destructuring/