1. 程式人生 > >ECMAScript6新特性簡介

ECMAScript6新特性簡介

原文:http://blog.gejiawen.com/2015/07/28/es6-new-feature/

ES6(ECMAScript 6)終於在2015年6月正式釋出了。距離上一次正式公開的ES5(於2009年釋出的)已經相距了不短的時間了。其實在ES6正式釋出之前的一段時間內,ECMA組織已經不再向ES6中新增新特性了,所以其實在ES6正式釋出之前,業界已經有許多對ES6的相關實踐了。比如阮一峰的ECMAScript 6入門其實就是早於ES6的正式釋出時間的。

本文將主要基於lukehoban/es6features,參考眾多的部落格文章資料,對新出的ES6中新增的特性做一個簡介。後續本系列將會產出針對ES6不同特性的文章。

ES6新特性列表

下面的表格給出了ES6包含的所有特性,

新增特性 關鍵詞 用法 描述
箭頭操作符 Arrows v => console.log(v) 類似於部分強型別語言中的lambda表示式
類的支援 Classes - 原生支援類,讓javascript的OOP編碼更加地道
增強的物件字面量 enhanced object literals - 增強物件字面量
字串模板 template strings ${num} 原生支援字串模板,不再需要第三方庫的支援
解構賦值 destructuring [x, y] = ['hello', 'world'] 使用過python的話,你應該很熟悉這個語法
函式引數擴充套件 default, rest, spread - 函式引數可以使用預設值、不定引數以及拓展引數了
let、const let、const - javascript中可以使用塊級作用域和宣告常量了
for…of遍歷 for…of for (v of someArray) { ... } 又多了一種折騰陣列、Map等資料結構的方法了
迭代器和生成器 iterators, generator, iterables - ES6較為難以理解的新東西,後面會有相關文章
Unicode unicode - 原生的unicode更加完美的支援
模組和模組載入 modules, modules loader - ES6中開始支援原生模組化啦
map, set, weakmap, weakset - - 新的資料結構
監控代理 proxies - 我們可以監聽物件發生了哪些事,並可以自定義對應的操作
Symbols - - 我們可以使用symbol來建立一個不同尋常的key
Promises - - 這傢伙經常在討論非同步處理流程時被提到
新的API math, number, string, array, object - 原生的功能性API就是方便些
內建物件可以被繼承 subclassable built-ins - 可以基於內建物件,比如Array,來生成一個類
二進位制、八進位制字面量 - - 可以直接在es6中使用二進位制或者八進位制字面量了
Reflect API - - 反射API?
尾呼叫 tail calls - ES6中會自動幫你做一些尾遞迴方面的優化

ok,上面就是es6中涉及到的所有的新增特性。下面我們針對每一個topic,舉一個sample example加以說明,不過並不會深入闡述。

箭頭操作符

何為箭頭操作符?其實箭頭操作符其實就是使用=>語法來代替函式。如果你熟悉C#或者Java之類的強型別語音,你應該知道lambda語法,其實箭頭操作符做的事情跟lambda表示式有異曲同工之妙。具體的用法看下面的例子,

123456789
var arr = [1, 2, 3];// 傳統寫法arr.forEach(function (v) {    console.log(v);});// 使用箭頭操作符arr.forEach( v => console.log(v));

在傳統寫法中,我們需要寫一個匿名的函式,然後給這個函式傳入引數,在函式體中對此引數做相關處理。而使用箭頭操作符後,它簡化了函式的編寫(其實是不需要再寫匿名函數了)。

箭頭操作符的左側可以接收一個簡單的引數,也可以使用一個引數列表。右側進行具體的操作或者是返回值。下面的示例展示了箭頭操作符更一般的用法,

123456789101112131415161718
// 在表示式中使用var odds = evens.map(v => v + 1);var nums = evens.map((v, i) => v + i);var pairs = evens.map(v => ({even: v, odd: v + 1}))// 在申明中使用nums.forEach(v => {    if (v % 5 === 0) fives.push(v);});// 使用this指標var bob = {    _name: "Bob",    _friends: [],    printFriends() {        this._friends.forEach(f => consol.log(this._name + " knows " + f));    }}

類的原生支援

ES6中提供了類的原生支援,引入了class關鍵字,其實它是一種實現OOP程式設計思想的語法糖。它是基於原型鏈的。

其實javascript本身就是面向物件的語音,只不過沒有Java之類的語言那麼學院派,它更加靈活。ES6中提供的類其實就是對原型模型的包裝。

ES6提供原生class支援後,類方面的操作,比如類的建立、繼承等等更加直觀了。並且父類方法的呼叫、例項化、靜態方法和建構函式等OOP概念都更加形象化了。

我們來看一些示例,看在ES6中到底如何使用學院化的使用類,

1234567891011121314151617181920212223242526272829303132333435363738
// 類的定義class Animal {    // ES6中的構造器,相當於建構函式    constructor(name) {        this.name = name;    }    // 例項方法    sayName() {        console.log('My Name is ' + this.name);    }}// 類的繼承class Programmer extends Animal {    constructor(name) {        // 直接呼叫父類構造器進行初始化        super(name);    }    // 子類自己的例項方法    program() {        console.log('I\'am coding...');    }    // 靜態方法    static LEVEL() {        console.log('LEVEL BAD!');    }}// 一些測試var doggy=new Animal('doggy'),larry=new Programmer('larry');doggy.sayName(); // ‘My name is doggy’larry.sayName(); // ‘My name is larry’larry.program(); // ‘I'm coding...’Programmer.LEVEL(); // ‘LEVEL BAD!’

可以看到,ES6中我們可以完全用Java中的思想來寫建立類、繼承類、例項方法、初始化等等。

物件字面量的增強

在ES6中,物件字面量被增強了,寫法更賤geek,同時可以在定義物件的時候做的事情更多了。比如,

  • 可以在物件字面量裡面定義原型
  • 定義方法可以不用function關鍵字了
  • 更加方便的定義方法
  • 直接呼叫原型鏈上層(父層)的方法

具體的我們看下面的示例程式碼,

12345678910111213141516171819202122
var human = {    breathe() {        console.log('breathing...');    }};function sleep() {    console.log('sleeping...');}var worker = {    __proto__: human, // 設定原型,相當於繼承human    company: 'code',    sleep, // 相當於 `sleep: sleep`    work() {        console.log('working...');    }};human.breathe(); // `breathing...`worker.sleep(); // `sleeping...`worker.work(); // `working...`

字串模板

ES6中提供原生的字串模板支援。其語法很簡單使用使用反引號`來建立字串模板,字串模板中可以使用${placeholder}來生成佔位符。

如果你有使用後端模板的經驗,比如smarty之類的,那麼你將對此不會太陌生。讓我們來看段示例,

12
var num = Math.random();console.log(`your random num is ${num}`);

字串模板其實並不是很難的東西,相對比較容易理解。個人覺得其目前主要的使用場景就是改變了動態生成字串的方式,不再像以前那樣使用字串拼接的方式。

解構賦值

如果你熟悉python等語言,你對解構賦值的概念應該很瞭解。解構的含義簡單點就是自動解析陣列或者物件中值,解析出來之後一次賦值給一系列的變數。

利用這個特性,我們可以讓一個函式返回一個數組,然後利用解構賦值得到陣列中的每一個元素。讓我們來看一些例子。

1234567891011121314
function getVal() {    return [1, 2];}var [x, y] = getValue();var [name, age] = ['larry', 26];console.log('x: ' + x + ', y: ' + y); // x: 1, y: 2console.log('name: ' + name + ', age: ' + age); // name: larry, age: 26// 交換兩個變數的值var [a, b] = [1, 2];[a, b] = [b, a];console.log('a: ' + a + ', b: ' + b);

函式引數的擴充套件

ES6中對函式引數作了很大的擴充套件,包括

  • 預設引數
  • 不定引數
  • 擴充套件引數

預設引數,即我們可以為函式的引數設定預設值了。可能你在某些js庫的原始碼經常會看到類似下面這樣的操作,

1234
function foo(name) {    name = name || 'larry';    console.log(name);}

上面的或操作其實就是為了給引數name設定預設值。

而在ES6中,我們可以採用更加方便的方式,如下,

123456
function foo(name = 'larry') {    console.log(name);}foo(); // 'larry'foo('gejiawen'); // 'gejiawen'

不定引數的意思是,我們的函式接受的引數是不確定的,由具體的呼叫時決定到底會有幾個引數。其實在ES6之前的規範中我們大可以使用arguments變數來達到不定引數的判斷,但是無疑這一方案過於複雜。

不定引數的用法是這樣的,我們在函式引數中使用...x這樣的方式來代表所有的引數。其中x的作用就是指代所有的不定引數集合(其實它就是一個數組)。讓我們來看個例子,

123456
function add(...x) {    return x.reduce((m, n) => m + n);}console.log(add(1, 2, 3)); // 6console.log(add(1, 2, 3, 4, 5)); // 15

關於拓展引數,其實拓展引數其實一種語法糖,它允許傳遞一個數組(或者類陣列)作為函式的引數,而不需要apply操作,函式內部可以自動將其當前不定引數解析,然後可以獲得每一個數組(類陣列)元素作為實際引數。看下面的示例,

12345
function foo(x, y, z) {    return x + y + z;}console.log(foo(...[1, 2, 3])); // 6

新增letconst關鍵字

ES6中新增了兩個關鍵字,分別為letconst

let用於宣告物件,作用類似var,但是let宣告的變數擁有更狹隘的作用域,脫離特定的作用域後變數的生命週期就終結了。這將有效的避免javascript中由於隱式變數作用域提升而導致的各種bug,甚至是記憶體洩露等等問題。

const用於宣告常量。

讓我們來看一些例子。

1234
for (let i = 0; i < 2; i++) {    console.log(i); // 0, 1}console.log(i); // undefined, 嚴格模式下會報錯

for...of遍歷

ES6中新增了一種遍歷陣列和物件的方法,叫做for...of。他與for...in用法相似。看下面的例子,

123456789
var arr = [1, 2, 3];for (let v of arr) {    console.log(v); // 1,2,3}for (let i in arr) {    console.log(arr[i]); // 1,2,3}

從上面的示例程式碼中,我們可以看出,for...of遍歷時提供的是value,而for...in遍歷提供的是key。

迭代器和生成器

ES6中新增的迭代器和生成器的相關概念相對來說有點複雜,後面將會產出專門的文章針對這個topic進行闡述。

Promise

Promises是處理非同步操作的一種模式,之前在很多三方庫中有實現,比如jQuery的deferred物件。當你發起一個非同步請求,並綁定了.when().done()等事件處理程式時,其實就是在應用promise模式。下面是一段示例程式碼,

123456789101112131415161718
//建立promisevar promise = new Promise(function(resolve, reject) {    // 進行一些非同步或耗時操作    if ( /*如果成功 */ ) {        resolve("Stuff worked!");    } else {        reject(Error("It broke"));    }});//繫結處理程式promise.then(function(result) {    //promise成功的話會執行這裡    console.log(result); // "Stuff worked!"}, function(err) {    //promise失敗會執行這裡    console.log(err); // Error: "It broke"});

關於promise更多的詳細內容,後面將會產出專門的文章針對這個topic進行闡述。

Unicode的支援

ES6中對Unicode的支援更加完善了,可以使用使用正則表示式的u標記對unicode字串進行匹配。

12345678910111213141516
// same as ES5.1"