Javascript ES6

ES6 全稱 ECMAScript 6

瀏覽器支援狀況

目前主流瀏覽器已大多支援 ES6,許多開發框架也紛紛宣布開始支援,如果想知道詳細支援狀況的話可以上 ECMAScript 6 compatibility table 查看。

回顧歷史,1996年11月,JavaScript 的創造者 Netscape 公司,決定將 JavaScript 提交給國際標準化組織ECMA,希望這種語言能夠成為國際標準。次年,ECMA 發布262號標準文件(ECMA-262)的第一版,規定了瀏覽器腳本語言的標準,並將這種語言稱為 ECMAScript,這個版本就是1.0版。

該標準從一開始就是針對 JavaScript 語言制定的,但是之所以不叫 JavaScript,有兩個原因。一是商標,Java 是 Sun 公司的商標,根據授權協議,只有 Netscape 公司可以合法地使用 JavaScript 這個名字,且 JavaScript 本身也已經被 Netscape 公司註冊為商標。二是想體現這門語言的制定者是 ECMA,不是 Netscape,這樣有利於保證這門語言的開放性和中立性。

因此,ECMAScript 和 JavaScript 的關係是,前者是後者的規格,後者是前者的一種實現(另外的 ECMAScript 方言還有 Jscript 和 ActionScript)。日常場合,這兩個詞是可以互換的。

ECMAScript 2015(簡稱 ES2015)這個詞,也是經常可以看到的。它與 ES6 是什麼關係呢?

2011年,ECMAScript 5.1版發布後,就開始制定6.0版了。因此,ES6 這個詞的原意,就是指 JavaScript 語言的下一個版本。

但是,因為這個版本引入的語法功能太多,而且制定過程當中,還有很多組織和個人不斷提交新功能。事情很快就變得清楚了,不可能在一個版本裡面包括所有將要引入的功能。常規的做法是先發布6.0版,過一段時間再發6.1版,然後是6.2版、6.3版等等。

但是,標準的制定者不想這樣做。他們想讓標準的升級成為常規流程:任何人在任何時候,都可以向標準委員會提交新語法的提案,然後標準委員會每個月開一次會,評估這些提案是否可以接受,需要哪些改進。如果經過多次會議以後,一個提案足夠成熟了,就可以正式進入標準了。這就是說,標準的版本升級成為了一個不斷滾動的流程,每個月都會有變動。

標準委員會最終決定,標準在每年的6月份正式發布一次,作為當年的正式版本。接下來的時間,就在這個版本的基礎上做改動,直到下一年的6月份,草案就自然變成了新一年的版本。這樣一來,就不需要以前的版本號了,只要用年份標記就可以了。

ES6 的第一個版本,就這樣在2015年6月發布了,正式名稱就是《ECMAScript 2015標準》(簡稱 ES2015)。2016年6月,小幅修訂的《ECMAScript 2016標準》(簡稱 ES2016)如期發布,這個版本可以看作是 ES6.1 版,因為兩者的差異非常小,基本上是同一個標準。根據計劃,2017年6月發布 ES2017 標準。

因此,ES6 既是一個歷史名詞,也是一個泛指,含義是5.1版以後的 JavaScript 的下一代標準,涵蓋了ES2015、ES2016、ES2017等等,而ES2015 則是正式名稱,特指該年發布的正式版本的語言標準。本書中提到 ES6 的地方,一般是指 ES2015 標準,但有時也是泛指「下一代 JavaScript 語言」。

ES6 從開始制定到最後發布,整整用了15年。

ECMAScript 1.0是1997年發布的,接下來的兩年,連續發布了 ECMAScript 2.0(1998年6月)和 ECMAScript 3.0(1999年12月)。3.0版是一個巨大的成功,在業界得到廣泛支持,成為通行標準,奠定了 JavaScript 語言的基本語法,以後的版本完全繼承。直到今天,初學者一開始學習 JavaScript,其實就是在學3.0版的語法。

2000年,ECMAScript 4.0開始醞釀。這個版本最後沒有通過,但是它的大部分內容被ES6繼承了。因此,ES6制定的起點其實是2000年。

為什麼ES4沒有通過呢?因為這個版本太激進了,對ES3做了徹底升級,導致標準委員會的一些成員不願意接受。ECMA的第39號技術專家委員會(Technical Committee 39,簡稱TC39)負責制訂ECMAScript標準,成員包括Microsoft、Mozilla、Google等大公司。

2007年10月,ECMAScript 4.0版草案發布,本來預計次年8月發布正式版本。但是,各方對於是否通過這個標準,發生了嚴重分歧。以Yahoo、Microsoft、Google為首的大公司,反對JavaScript的大幅升級,主張小幅改動;以JavaScript創造者Brendan Eich為首的Mozilla公司,則堅持當前的草案。

2008年7月,由於對於下一個版本應該包括哪些功能,各方分歧太大,爭論過於激烈,ECMA開會決定,中止ECMAScript 4.0的開發,將其中涉及現有功能改善的一小部分,發布為ECMAScript 3.1,而將其他激進的設想擴大範圍,放入以後的版本,由於會議的氣氛,該版本的項目代號起名為Harmony(和諧)。會後不久,ECMAScript 3.1就改名為ECMAScript 5。

2009年12月,ECMAScript 5.0版正式發布。Harmony項目則一分為二,一些較為可行的設想定名為JavaScript.next繼續開發,後來演變成ECMAScript 6;一些不是很成熟的設想,則被視為JavaScript.next.next,在更遠的將來再考慮推出。TC39委員會的總體考慮是,ES5與ES3基本保持兼容,較大的語法修正和新功能加入,將由JavaScript.next完成。當時,JavaScript.next指的是ES6,第六版發布以後,就指ES7。TC39的判斷是,ES5會在2013年的年中成為JavaScript開發的主流標準,並在此後五年中一直保持這個位置。

2011年6月,ECMAscript 5.1版發布,並且成為ISO國際標準(ISO/IEC 16262:2011)。

2013年3月,ECMAScript 6草案凍結,不再添加新功能。新的功能設想將被放到ECMAScript 7。

2013年12月,ECMAScript 6草案發布。然後是12個月的討論期,聽取各方反饋。

2015年6月,ECMAScript 6正式通過,成為國際標準。從2000年算起,這時已經過去了15年。

原文網址:https://kknews.cc/news/b2kbmz6.html

ECMAScript 是由ECMA -262標準化的手稿語言的名稱。 EcmaScript是一種由ECMA國際(前身為歐洲電腦製造商協會(European Computer Manufacturers Association))在標準ECMA-262中定義的手稿語言規範。這種語言在全球資訊網上應用廣泛,它往往被稱為JavaScriptJScript,但實際上後兩者是ECMA-262標準的實現和擴充。

大部分學習過程式語言的人都聽過Javascript,但卻很少人聽聞過ECMA-262。ECMA是個國際標準,ECMA-262可以說是一個規範書,他定義著一種叫做ECMAScript的腳本語言。ECMAScript是基於許多原創技術而建立,其中包含Nescape的javascript,以及微軟的JScript 。javascript最早是由網景公司(Nescape)的Brendan Eich所發明,並且送交國際組織進行標準化。Brendan Eich在1995年4月,任職於網景期間,為網景瀏覽器開發出JavaScript,之後成為網頁瀏覽器領域應用最廣泛的手稿語言之一。 1998年,艾克協助成立Mozilla.org,2003年在美國線上決定結束網景部門營運後,艾克協助成立了Mozilla基金會

這標準化的結果就是ECMA-262,在ECMA-262第三版發行以來,ECMA腳本語言已獲得了大規模的採納,並藉以搭配全球資訊網。在全球資訊網中,ECMA腳本語言已成為實質上所有瀏覽器所支援的程式語言。

ECMA-262定義的ECMAScript源於Javascript,當他成為一個國際標準時,所有宣稱符合ECMA-262的腳本語言,包含Javascript或是JScript,都應該遵守其中的規範,但可以超出其未規範的部分,由此可見ECMA-262可以說是Javascript的最核心部分。

ECMA-262的第一版出現在1997年的6月,一直到2011年6月出現了第5版。ECMA-262第五版出現了一個新的"嚴格模式(strict mode)",嚴格模式(strict mode)限制了許多Javascript原本的用法,最主要就是為了減少Javascript最讓人詬病的缺點,過於鬆散的結構,不安全,易發生錯誤等等。

ECMA-262是定義了Javascript的核心規範。

image

Ref:

  1. ECMAScript - 维基百科,自由的百科全书
  2. 何謂ECMA-262
  3. Our Thinking - Emids
  4. ECMASCRIPT. Before going to see about ECMASCRIPT… | by Swarna Ramesh | Medium
  5. ES6 Next Generation JavaScript | Course
  6. https://kknews.cc/zh-tw/news/b2kbmz6.html

最常用的ES6特性

let, const, class, extends, super, arrow functions, template string, destructuring, default, rest arguments
這些是ES6最常用的幾個語法。

let, const

這兩個的用途與var類似,都是用來聲明變數的,但在實際運用中他倆都有各自的特殊用途。
首先來看下面這個例子:

var name = 'zach'

while (true) {
    var name = 'obama'
    console.log(name)  //obama
    break
}

console.log(name)  //obama

使用var 兩次輸出都是obama,這是因為ES5只有全域作用域和函數作用域,沒有塊級作用域,這帶來很多不合理的場景。第一種場景就是你現在看到的內層變數覆蓋外層變數。而let則實際上為JavaScript新增了塊級作用域。用它所聲明的變數,只在let命令所在的代碼塊內有效。

let name = 'zach'

while (true) {
    let name = 'obama'
    console.log(name)  //obama
    break
}

console.log(name)  //zach

另外一個var帶來的不合理場景就是用來計數的迴圈變數洩露為全域變數,看下面的例子:

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10

上面代碼中,變數i是var聲明的,在全域範圍內都有效。所以每一次迴圈,新的i值都會覆蓋舊值,導致最後輸出的是最後一輪的i的值。而使用let則不會出現這個問題。

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6

再來看一個更常見的例子,瞭解下如果不用ES6,而用閉包如何解決這個問題。

var clickBoxs = document.querySelectorAll('.clickBox')
for (var i = 0; i < clickBoxs.length; i++){
    clickBoxs[i].onclick = function(){
        console.log(i)
    }
}

我們本來希望的是點擊不同的clickBox,顯示不同的i,但事實是無論我們點擊哪個clickBox,輸出的都是5。下面我們來看下,如何用閉包搞定它。

function iteratorFactory(i){
    var onclick = function(e){
        console.log(i)
    }
    return onclick;
}
var clickBoxs = document.querySelectorAll('.clickBox')
for (var i = 0; i < clickBoxs.length; i++){
    clickBoxs[i].onclick = iteratorFactory(i)
}

const也用來聲明變數,但是聲明的是常量。一旦聲明,常量的值就不能改變。

const PI = Math.PI

PI = 23 //Module build failed: SyntaxError: /es6/app.js: "PI" is read-only

當我們嘗試去改變用const聲明的常量時,流覽器就會報錯。
const有一個很好的應用場景,就是當我們引用協力廠商庫的時聲明的變數,用const來聲明可以避免未來不小心重命名而導致出現bug:

const monent = require('moment')

class, extends, super

這三個特性涉及了ES5中最令人頭疼的的幾個部分:原型、構造函數,繼承…你還在為它們複雜難懂的語法而煩惱嗎?你還在為指標到底指向哪裡而糾結萬分嗎?

有了ES6我們不再煩惱!

ES6提供了更接近傳統語言的寫法,引入了Class(類)這個概念。新的class寫法讓物件原型的寫法更加清晰、更像物件導向程式設計的語法,也更加通俗易懂。

class Animal {
    constructor(){
        this.type = 'animal'
    }
    says(say){
        console.log(this.type + ' says ' + say)
    }
}

let animal = new Animal()
animal.says('hello') //animal says hello

class Cat extends Animal {
    constructor(){
        super()
        this.type = 'cat'
    }
}

let cat = new Cat()
cat.says('hello') //cat says hello

上面代碼首先用class定義了一個“類”,可以看到裡面有一個constructor方法,這就是構造方法,而this關鍵字則代表實例物件。簡單地說,constructor內定義的方法和屬性是實例物件自己的,而constructor外定義的方法和屬性則是所有實例物件可以共用的。

Class之間可以通過extends關鍵字實現繼承,這比ES5的通過修改原型鏈實現繼承,要清晰和方便很多。上面定義了一個Cat類,該類通過extends關鍵字,繼承了Animal類的所有屬性和方法。

super關鍵字,它指代父類的實例(即父類的this物件)。子類必須在constructor方法中調用super方法,否則新建實例時會報錯。這是因為子類沒有自己的this物件,而是繼承父類的this對象,然後對其進行加工。如果不調用super方法,子類就得不到this物件。

ES6的繼承機制,實質是先創造父類的實例物件this(所以必須先調用super方法),然後再用子類的構造函數修改this。

arrow function

這個恐怕是ES6最最常用的一個新特性了,用它來寫function比原來的寫法要簡潔清晰很多:

function(i){ return i + 1; } //ES5
(i) => i + 1 //ES6

簡直是簡單的不像話對吧…
如果方程比較複雜,則需要用{}把代碼包起來:

function(x, y) { 
    x++;
    y--;
    return x + y;
}
(x, y) => {x++; y--; return x+y}

除了看上去更簡潔以外,arrow function還有一項超級無敵的功能!
長期以來,JavaScript語言的this物件一直是一個令人頭痛的問題,在物件方法中使用this,必須非常小心。例如:

class Animal {
    constructor(){
        this.type = 'animal'
    }
    says(say){
        setTimeout(function(){
            console.log(this.type + ' says ' + say)
        }, 1000)
    }
}

 var animal = new Animal()
 animal.says('hi')  //undefined says hi

運行上面的代碼會報錯,這是因為setTimeout中的this指向的是全域物件。所以為了讓它能夠正確的運行,傳統的解決方法有兩種:

  1. 第一種是將this傳給self,再用self來指代thissays(say){
var self = this;
setTimeout(function(){
    console.log(self.type + ' says ' + say)
}, 1000)

2.第二種方法是用bind(this),即

    says(say){
        setTimeout(function(){
            console.log(this.type + ' says ' + say)
        }.bind(this), 1000)

但現在我們有了箭頭函數,就不需要這麼麻煩了:

class Animal {
    constructor(){
        this.type = 'animal'
    }
    says(say){
        setTimeout( () => {
            console.log(this.type + ' says ' + say)
        }, 1000)
    }
}
 var animal = new Animal()
 animal.says('hi')  //animal says hi

當我們使用箭頭函數時,函數體內的this物件,就是定義時所在的物件,而不是使用時所在的物件。
並不是因為箭頭函數內部有綁定this的機制,實際原因是箭頭函數根本沒有自己的this,它的this是繼承外面的,因此內部的this就是外層代碼塊的this。

template string

這個東西也是非常有用,當我們要插入大段的html內容到文檔中時,傳統的寫法非常麻煩,所以之前我們通常會引用一些範本工具庫,比如mustache等等。

大家可以先看下面一段代碼:

$("#result").append(
  "There are <b>" + basket.count + "</b> " +
  "items in your basket, " +
  "<em>" + basket.onSale +
  "</em> are on sale!"
);

我們要用一堆的’+'號來連接文本與變數,而使用ES6的新特性範本字串``後,我們可以直接這麼來寫:

$("#result").append(`
  There are <b>${basket.count}</b> items
   in your basket, <em>${basket.onSale}</em>
  are on sale!
`);

用反引號(Backticks) (`)來標識起始,用${}來引用變數,而且所有的空格和縮進都會被保留在輸出之中,是不是非常爽?!

React Router從第1.0.3版開始也使用ES6語法了,比如這個例子:

<Link to={`/taco/${taco.name}`}>{taco.name}</Link>

destructuring

ES6允許按照一定模式,從陣列和物件中提取值,對變數進行賦值,這被稱為解構(Destructuring)。

看下面的例子:

let cat = 'ken'
let dog = 'lili'
let zoo = {cat: cat, dog: dog}
console.log(zoo)  //Object {cat: "ken", dog: "lili"}

用ES6完全可以像下面這麼寫:

let cat = 'ken'
let dog = 'lili'
let zoo = {cat, dog}
console.log(zoo)  //Object {cat: "ken", dog: "lili"}

反過來可以這麼寫:

let dog = {type: 'animal', many: 2}
let { type, many} = dog
console.log(type, many)   //animal 2

default, rest

default很簡單,意思就是預設值。大家可以看下面的例子,調用animal()方法時忘了傳參數,傳統的做法就是加上這一句type = type || 'cat' 來指定預設值。

function animal(type){
    type = type || 'cat'  
    console.log(type)
}
animal()

如果用ES6我們而已直接這麼寫:

function animal(type = 'cat'){
    console.log(type)
}
animal()

最後一個rest語法也很簡單,直接看例子:

function animals(...types){
    console.log(types)
}
animals('cat', 'dog', 'fish') //["cat", "dog", "fish"]

而如果不用ES6的話,我們則得使用ES5的arguments

總結

以上就是ES6最常用的一些語法,可以說這20%的語法,在ES6的日常使用中占了80%…