ES6(ECMAScript2015)的出現,無疑給前端開發人員帶來了新的驚喜,它包含了一些很棒的新特性,可以更加方便的實現很多復雜的操作,提高開發人員的效率。
本文主要針對ES6做一個簡要介紹。 主要譯自: http://webapplog.com/ES6/comment-page-1/。也許你還不知道ES6是什么, 實際上, 它是一種新的javascript規范。在這個大家都很忙碌的時代,如果你想對ES6有一個快速的了解,那么請繼續往下讀,去了解當今最流行的編程語言JavaScript最新一代的十大特性。
以下是ES6排名前十的最佳特性列表(排名不分先后):
Default Parameters(默認參數) in ES6
Template Literals (模板文本)in ES6
Multi-line Strings (多行字符串)in ES6
Destructuring Assignment (解構賦值)in ES6
Enhanced Object Literals (增強的對象文本)in ES6
Arrow Functions (箭頭函數)in ES6
Promises in ES6
Block-Scoped Constructs Let and Const(塊作用域構造Let and Const)
Classes(類) in ES6
Modules(模塊) in ES6
聲明:這些列表僅是個人主觀意見。它絕不是為了削弱ES6其它功能,這里只列出了10條比較常用的特性。
首先回顧一下JavaScript的歷史,不清楚歷史的人,很難理解JavaScript為什么會這樣發展。下面就是一個簡單的JavaScript發展時間軸:
1、1995:JavaScript誕生,它的初始名叫LiveScript。
2、1997:ECMAScript標準確立。
3、1999:ES3出現,與此同時IE5風靡一時。
4、2000–2005: XMLHttpRequest又名AJAX, 在Outlook Web Access (2000)、Oddpost (2002),Gmail (2004)和Google Maps (2005)大受重用。
5、2009: ES5出現,(就是我們大多數人現在使用的)例如foreach,Object.keys,Object.create和JSON標準。
6、2015:ES6/ECMAScript2015出現。
歷史回顧就先到此,現讓我們進入正題。
1.Default Parameters(默認參數) in ES6
還記得我們以前不得不通過下面方式來定義默認參數:
var link = function (height, color, url) {
var height = height || 50;
var color = color || 'red';
var url = url || 'http://azat.co';
...
}
一切工作都是正常的,直到參數值是0后,就有問題了,因為在JavaScript中,0表示fasly,它是默認被hard-coded的值,而不能變成參數本身的值。當然,如果你非要用0作為值,我們可以忽略這一缺陷并且使用邏輯OR就行了!但在ES6,我們可以直接把默認值放在函數申明里:
var link = function(height = 50, color = 'red', url = 'http://azat.co') {
? ...
}
順便說一句,這個語法類似于Ruby!
2.Template Literals(模板對象) in ES6
在其它語言中,使用模板和插入值是在字符串里面輸出變量的一種方式。因此,在ES5,我們可以這樣組合一個字符串:
var name = 'Your name is ' + first + ' ' + last + '.';
var url = 'http://localhost:3000/api/messages/' + id;
幸運的是,在ES6中,我們可以使用新的語法$ {NAME},并把它放在反引號里:
var name = `Your name is ${first} ${last}. `;
var url = `http://localhost:3000/api/messages/${id}`;
3.Multi-line Strings (多行字符串)in ES6
ES6的多行字符串是一個非常實用的功能。在ES5中,我們不得不使用以下方法來表示多行字符串:
var roadPoem = 'Then took the other, as just as fair,nt'
? ? + 'And having perhaps the better claimnt'
? ? + 'Because it was grassy and wanted wear,nt'
? ? + 'Though as for that the passing therent'
? ? + 'Had worn them really about the same,nt';
var fourAgreements = 'You have the right to be you.n
? ? You can only be you when you do your best.';
然而在ES6中,僅僅用反引號就可以解決了:
var roadPoem = `Then took the other, as just as fair,
? ? And having perhaps the better claim
? ? Because it was grassy and wanted wear,
? ? Though as for that the passing there
? ? Had worn them really about the same,`;
var fourAgreements = `You have the right to be you.
? ? You can only be you when you do your best.`;
4.Destructuring Assignment (解構賦值)in ES6
解構可能是一個比較難以掌握的概念。先從一個簡單的賦值講起,其中house 和 mouse是key,同時house 和mouse也是一個變量,在ES5中是這樣:
var data = $('body').data(), // data has properties house and mouse
house = data.house,
mouse = data.mouse;
以及在node.js中用ES5是這樣:
var jsonMiddleware = require('body-parser').jsonMiddleware ;
var body = req.body, // body has username and password
? username = body.username,
? password = body.password;
在ES6,我們可以使用這些語句代替上面的ES5代碼:
var { house, mouse} = $('body').data(); // we'll get house and mouse variables
var {jsonMiddleware} = require('body-parser');
var {username, password} = req.body;
這個同樣也適用于數組,非常贊的用法:
var [col1, col2]? = $('.column'),
? [line1, line2, line3, , line5] = file.split('n');
我們可能需要一些時間來習慣解構賦值語法的使用,但是它確實能給我們帶來許多意外的收獲。
5.Enhanced Object Literals (增強的對象字面量)in ES6
使用對象文本可以做許多讓人意想不到的事情!通過ES6,我們可以把ES5中的JSON變得更加接近于一個類。
下面是一個典型ES5對象文本,里面有一些方法和屬性:
var serviceBase = {port: 3000, url: 'azat.co'},
getAccounts = function(){return [1,2,3]};
var accountServiceES5 = {
port: serviceBase.port,
url: serviceBase.url,
getAccounts: getAccounts,
toString: function() {
return JSON.stringify(this.valueOf());
},
getUrl: function() {return "http://" + this.url + ':' + this.port},
valueOf_1_2_3: getAccounts()
}
如果我們想讓它更有意思,我們可以用Object.create從serviceBase繼承原型的方法:
var accountServiceES5ObjectCreate = Object.create(serviceBase)
var accountServiceES5ObjectCreate = {
? getAccounts: getAccounts,
? toString: function() {
? ? return JSON.stringify(this.valueOf());
? },
? getUrl: function() {return "http://" + this.url + ':' + this.port},
? valueOf_1_2_3: getAccounts()
}
我們知道,accountServiceES5ObjectCreate 和accountServiceES5 并不是完全一致的,因為一個對象(accountServiceES5)在proto對象中將有下面這些屬性:
new1
為了方便舉例,我們將考慮它們的相似處。所以在ES6的對象文本中,既可以直接分配getAccounts: getAccounts,也可以只需用一個getAccounts,此外,我們在這里通過proto(并不是通過’proto’)設置屬性,如下所示:
var serviceBase = {port: 3000, url: 'azat.co'},
getAccounts = function(){return [1,2,3]};
var accountService = {
__proto__: serviceBase,
getAccounts,
另外,我們可以調用super防范,以及使用動態key值(valueOf_1_2_3):
toString() {
return JSON.stringify((super.valueOf()));
},
getUrl() {return "http://" + this.url + ':' + this.port},
[ 'valueOf_' + getAccounts().join('_') ]: getAccounts()
};
console.log(accountService)
new2
ES6對象文本是一個很大的進步對于舊版的對象文本來說。
6.Arrow Functions in(箭頭函數) ES6
這是我迫不及待想講的一個特征,CoffeeScript 就是因為它豐富的箭頭函數讓很多開發者喜愛。在ES6中,也有了豐富的箭頭函數。這些豐富的箭頭是令人驚訝的因為它們將使許多操作變成現實,比如,
以前我們使用閉包,this總是預期之外地產生改變,而箭頭函數的迷人之處在于,現在你的this可以按照你的預期使用了,身處箭頭函數里面,this還是原來的this。
有了箭頭函數在ES6中, 我們就不必用that = this或 self = this 或 _this = this 或.bind(this)。例如,下面的代碼用ES5就不是很優雅:
var _this = this;
$('.btn').click(function(event){
? _this.sendData();
})
在ES6中就不需要用 _this = this:
$('.btn').click((event) =>{
? this.sendData();
})
不幸的是,ES6委員會決定,以前的function的傳遞方式也是一個很好的方案,所以它們仍然保留了以前的功能。
下面這是一個另外的例子,我們通過call傳遞文本給logUpperCase() 函數在ES5中:
var logUpperCase = function() {
? var _this = this;
? this.string = this.string.toUpperCase();
? return function () {
? ? return console.log(_this.string);
? }
}
logUpperCase.call({ string: 'ES6 rocks' })();
而在ES6,我們并不需要用_this浪費時間:
var logUpperCase = function() {
? this.string = this.string.toUpperCase();
? return () => console.log(this.string);
}
logUpperCase.call({ string: 'ES6 rocks' })();
請注意,只要你愿意,在ES6中=>可以混合和匹配老的函數一起使用。當在一行代碼中用了箭頭函數,它就變成了一個表達式。它將暗地里返回單個語句的結果。如果你超過了一行,將需要明確使用return。
這是用ES5代碼創建一個消息數組:
var ids = ['5632953c4e345e145fdf2df8','563295464e345e145fdf2df9'];
var messages = ids.map(function (value) {
? return "ID is " + value; // explicit return
});
用ES6是這樣:
var ids = ['5632953c4e345e145fdf2df8','563295464e345e145fdf2df9'];
var messages = ids.map(value => `ID is ${value}`); // implicit return
請注意,這里用了字符串模板。
在箭頭函數中,對于單個參數,括號()是可選的,但當你超過一個參數的時候你就需要他們。
在ES5代碼有明確的返回功能:
var ids = ['5632953c4e345e145fdf2df8', '563295464e345e145fdf2df9'];
var messages = ids.map(function (value, index, list) {
? return 'ID of ' + index + ' element is ' + value + ' '; // explicit return
});
在ES6中有更加嚴謹的版本,參數需要被包含在括號里并且它是隱式的返回:
var ids = ['5632953c4e345e145fdf2df8','563295464e345e145fdf2df9'];
var messages = ids.map((value, index, list) => `ID of ${index} element is ${value} `); // implicit return
7. Promises in ES6
Promises 是一個有爭議的話題。因此有許多略微不同的promise 實現語法。Q,bluebird,deferred.js,vow, avow, jquery 一些可以列出名字的。也有人說我們不需要promises,僅僅使用異步,生成器,回調等就夠了。但令人高興的是,在ES6中有標準的Promise實現。
下面是一個簡單的用setTimeout()實現的異步延遲加載函數:
setTimeout(function(){
? console.log('Yay!');
}, 1000);
在ES6中,我們可以用promise重寫:
var wait1000 =? new Promise(function(resolve, reject) {
? setTimeout(resolve, 1000);
}).then(function() {
? console.log('Yay!');
});
或者用ES6的箭頭函數:
var wait1000 =? new Promise((resolve, reject)=> {
? setTimeout(resolve, 1000);
}).then(()=> {
? console.log('Yay!');
});
到目前為止,代碼的行數從三行增加到五行,并沒有任何明顯的好處。確實,如果我們有更多的嵌套邏輯在setTimeout()回調函數中,我們將發現更多好處:
setTimeout(function(){
? console.log('Yay!');
? setTimeout(function(){
? ? console.log('Wheeyee!');
? }, 1000)
}, 1000);
在ES6中我們可以用promises重寫:
var wait1000 =? ()=> new Promise((resolve, reject)=> {setTimeout(resolve, 1000)});
wait1000()
? ? .then(function() {
? ? ? ? console.log('Yay!')
? ? ? ? return wait1000()
? ? })
? ? .then(function() {
? ? ? ? console.log('Wheeyee!')
? ? });
還是不確信Promises 比普通回調更好?其實我也不確信,我認為一旦你有回調的想法,那么就沒有必要額外增加promises的復雜性。
雖然,ES6 有讓人崇拜的Promises 。Promises 是一個有利有弊的回調但是確實是一個好的特性,更多詳細的信息關于promise:Introduction to ES6 Promises.
8.Block-Scoped Constructs Let and Const(塊作用域和構造let和const)**
在ES6代碼中,你可能已經看到那熟悉的身影let。在ES6里let并不是一個花俏的特性,它是更復雜的。Let是一種新的變量申明方式,它允許你把變量作用域控制在塊級里面。我們用大括號定義代碼塊,在ES5中,塊級作用域起不了任何作用:
function calculateTotalAmount (vip) {
? var amount = 0;
? if (vip) {
? ? var amount = 1;
? }
? { // more crazy blocks!
? ? var amount = 100;
? ? {
? ? ? var amount = 1000;
? ? }
? }
? return amount;
}
console.log(calculateTotalAmount(true));
結果將返回1000,這真是一個bug。在ES6中,我們用let限制塊級作用域。而var是限制函數作用域。
function calculateTotalAmount (vip) {
? var amount = 0; // probably should also be let, but you can mix var and let
? if (vip) {
? ? let amount = 1; // first amount is still 0
? }
? { // more crazy blocks!
? ? let amount = 100; // first amount is still 0
? ? {
? ? ? let amount = 1000; // first amount is still 0
? ? }
? }
? return amount;
}
console.log(calculateTotalAmount(true));
這個結果將會是0,因為塊作用域中有了let。如果(amount=1).那么這個表達式將返回1。談到const,就更加容易了;它就是一個不變量,也是塊級作用域就像let一樣。下面是一個演示,這里有一堆常量,它們互不影響,因為它們屬于不同的塊級作用域:
function calculateTotalAmount (vip) {
? const amount = 0;
? if (vip) {
? ? const amount = 1;
? }
? { // more crazy blocks!
? ? const amount = 100 ;
? ? {
? ? ? const amount = 1000;
? ? }
? }
? return amount;
}
console.log(calculateTotalAmount(true));
從我個人看來,let 和const使這個語言變復雜了。沒有它們的話,我們只需考慮一種方式,現在有許多種場景需要考慮。
9. Classes (類)in ES6
如果你喜歡面向對象編程(OOP),那么你將喜愛這個特性。以后寫一個類和繼承將變得跟在facebook上寫一個評論那么容易。
類的創建和使用真是一件令人頭疼的事情在過去的ES5中,因為沒有一個關鍵字class (它被保留,但是什么也不能做)。在此之上,大量的繼承模型像pseudo classical, classical, functional 更加增加了混亂,JavaScript 之間的宗教戰爭只會更加火上澆油。
用ES5寫一個類,有很多種方法,這里就先不說了。現在就來看看如何用ES6寫一個類吧。ES6沒有用函數, 而是使用原型實現類。我們創建一個類baseModel ,并且在這個類里定義了一個constructor 和一個 getName()方法:
class baseModel {
? constructor(options, data) { // class constructor,node.js 5.6暫時不支持options = {}, data = []這樣傳參
? ? this.name = 'Base';
? ? this.url = 'http://azat.co/api';
? ? this.data = data;
? ? this.options = options;
? }
? ? getName() { // class method
? ? ? ? console.log(`Class name: ${this.name}`);
? ? }
}
注意我們對options 和data使用了默認參數值。此外方法名也不需要加function關鍵字,而且冒號(:)也不需要了。另外一個大的區別就是你不需要分配屬性this。現在設置一個屬性的值,只需簡單的在構造函數中分配。
AccountModel 從類baseModel 中繼承而來:
class AccountModel extends baseModel {
? ? constructor(options, data) {
為了調用父級構造函數,可以毫不費力的喚起super()用參數傳遞:
? ? ? super({private: true}, ['32113123123', '524214691']); //call the parent method with super
? ? ? this.name = 'Account Model';
? ? ? this.url +='/accounts/';
? ? }
如果你想做些更好玩的,你可以把 accountData 設置成一個屬性:
? ? get accountsData() { //calculated attribute getter
? ? // ... make XHR
? ? ? ? return this.data;
? ? }
}
那么,你如何調用他們呢?它是非常容易的:
let accounts = new AccountModel(5);
accounts.getName();
console.log('Data is %s', accounts.accountsData);
結果令人驚訝,輸出是:
Class name: Account Model
Data is? 32113123123,524214691
10. Modules (模塊)in ES6
眾所周知,在ES6以前JavaScript并不支持本地的模塊。人們想出了AMD,RequireJS,CommonJS以及其它解決方法。現在ES6中可以用模塊import 和export 操作了。
在ES5中,你可以在
module.exports = {
? port: 3000,
? getAccounts: function() {
? ? ...
? }
}
在ES5中,main.js需要依賴require(‘module’) 導入module.js:
var service = require('module.js');
console.log(service.port); // 3000
但在ES6中,我們將用export and import。例如,這是我們用ES6 寫的module.js文件庫:
export var port = 3000;
export function getAccounts(url) {
? ...
}
如果用ES6來導入到文件main.js中,我們需用import {name} from ‘my-module’語法,例如:
import {port, getAccounts} from 'module';
console.log(port); // 3000
或者我們可以在main.js中把整個模塊導入, 并命名為 service:
import * as service from 'module';
console.log(service.port); // 3000
從我個人角度來說,我覺得ES6模塊是讓人困惑的。但可以肯定的事,它們使語言更加靈活了。
并不是所有的瀏覽器都支持ES6模塊,所以你需要使用一些像jspm去支持ES6模塊。
更多的信息和例子關于ES6模塊,請看 this text。不管怎樣,請寫模塊化的JavaScript。
如何使用ES6 (Babel)
ES6已經敲定,但并不是所有的瀏覽器都完全支持,詳見:http://kangax.github.io/compat-table/es6/。要使用ES6,需要一個編譯器例如:babel。你可以把它作為一個獨立的工具使用,也可以把它放在構建中。grunt,gulp和webpack中都有可以支持babel的插件。
6941baebjw1f61bokq3xbj20yb0pdq6a
這是一個gulp案列,安裝gulp-babel插件:
$ npm install --save-dev gulp-babel
在gulpfile.js中,定義一個任務build,放入src/app.js,并且編譯它進入構建文件中。
var gulp = require('gulp'),
? babel = require('gulp-babel');
gulp.task('build', function () {
? return gulp.src('src/app.js')
? ? .pipe(babel())
? ? .pipe(gulp.dest('build'));
})
Node.js and ES6
在nodejs中,你可以用構建工具或者獨立的Babel模塊 babel-core 來編譯你的Node.js文件。安裝如下:
$ npm install --save-dev babel-core
然后在node.js中,你可以調用這個函數:
require("babel-core").transform(ES5Code, options);
ES6總結
這里還有許多ES6的其它特性你可能會使用到,排名不分先后:
1、全新的Math, Number, String, Array 和 Object 方法
2、二進制和八進制數據類型
3、默認參數不定參數擴展運算符
4、Symbols符號
5、tail調用
6、Generators (生成器)
7、New data structures like Map and Set(新的數據構造對像MAP和set)
參考文獻:
ES6 Cheatsheet (FREE PDF)
http://webapplog.com/ES6/comment-page-1/
Understanding ECMAScript 6by Nicolas Zakas book
http://ES6-features.org/#DateTimeFormatting
IIFE:立刻運行的函數表達式
中直接寫可以運行的代碼(簡稱IIFE),或者一些庫像AMD。然而在ES6中,你可以用export導入你的類。下面舉個例子,在ES5中,module.js有port變量和getAccounts 方法: