ES6(ECMAScript2015)的出現(xiàn)戏挡,無疑給前端開發(fā)人員帶來了新的驚喜爷辙,它包含了一些很棒的新特性课竣,可以更加方便的實現(xiàn)很多復雜的操作,提高開發(fā)人員的效率叉信。
本文主要針對ES6做一個簡要介紹亩冬。 主要譯自: http://webapplog.com/ES6/comment-page-1/。也許你還不知道ES6是什么, 實際上, 它是一種新的javascript規(guī)范硼身。在這個大家都很忙碌的時代硅急,如果你想對ES6有一個快速的了解,那么請繼續(xù)往下讀佳遂,去了解當今最流行的編程語言JavaScript最新一代的十大特性营袜。
以下是ES6排名前十的最佳特性列表(排名不分先后):
Default Parameters(默認參數(shù)) in ES6
Template Literals (模板文本)in ES6
Multi-line Strings (多行字符串)in ES6
Destructuring Assignment (解構(gòu)賦值)in ES6
Enhanced Object Literals (增強的對象文本)in ES6
Arrow Functions (箭頭函數(shù))in ES6
Promises in ES6
Block-Scoped Constructs Let and Const(塊作用域構(gòu)造Let and Const)
Classes(類) in ES6
Modules(模塊) in ES6
聲明:這些列表僅是個人主觀意見。它絕不是為了削弱ES6其它功能讶迁,這里只列出了10條比較常用的特性连茧。
首先回顧一下JavaScript的歷史,不清楚歷史的人巍糯,很難理解JavaScript為什么會這樣發(fā)展啸驯。下面就是一個簡單的JavaScript發(fā)展時間軸:
1、1995:JavaScript誕生祟峦,它的初始名叫LiveScript罚斗。
2、1997:ECMAScript標準確立宅楞。
3针姿、1999:ES3出現(xiàn)袱吆,與此同時IE5風靡一時。
4距淫、2000–2005: XMLHttpRequest又名AJAX绞绒, 在Outlook Web Access (2000)、Oddpost (2002)榕暇,Gmail (2004)和Google Maps (2005)大受重用蓬衡。
5、2009: ES5出現(xiàn)彤枢,(就是我們大多數(shù)人現(xiàn)在使用的)例如foreach狰晚,Object.keys,Object.create和JSON標準缴啡。
6壁晒、2015:ES6/ECMAScript2015出現(xiàn)。
歷史回顧就先到此业栅,現(xiàn)讓我們進入正題秒咐。
1.Default Parameters(默認參數(shù)) in ES6
還記得我們以前不得不通過下面方式來定義默認參數(shù):
var link = function (height, color, url) {
var height = height || 50;
var color = color || 'red';
var url = url || 'http://azat.co';
...
}
一切工作都是正常的,直到參數(shù)值是0后式镐,就有問題了反镇,因為在JavaScript中,0表示fasly娘汞,它是默認被hard-coded的值歹茶,而不能變成參數(shù)本身的值。當然你弦,如果你非要用0作為值惊豺,我們可以忽略這一缺陷并且使用邏輯OR就行了!但在ES6禽作,我們可以直接把默認值放在函數(shù)申明里:
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 (解構(gòu)賦值)in ES6
解構(gòu)可能是一個比較難以掌握的概念潮尝。先從一個簡單的賦值講起榕吼,其中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;
這個同樣也適用于數(shù)組,非常贊的用法:
var [col1, col2]? = $('.column'),
? [line1, line2, line3, , line5] = file.split('n');
我們可能需要一些時間來習慣解構(gòu)賦值語法的使用戴质,但是它確實能給我們帶來許多意外的收獲度宦。
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,
另外甘畅,我們可以調(diào)用super防范,以及使用動態(tài)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(箭頭函數(shù)) ES6
這是我迫不及待想講的一個特征疏唾,CoffeeScript 就是因為它豐富的箭頭函數(shù)讓很多開發(fā)者喜愛。在ES6中函似,也有了豐富的箭頭函數(shù)槐脏。這些豐富的箭頭是令人驚訝的因為它們將使許多操作變成現(xiàn)實,比如撇寞,
以前我們使用閉包顿天,this總是預期之外地產(chǎn)生改變,而箭頭函數(shù)的迷人之處在于蔑担,現(xiàn)在你的this可以按照你的預期使用了牌废,身處箭頭函數(shù)里面,this還是原來的this钟沛。
有了箭頭函數(shù)在ES6中畔规, 我們就不必用that = this或 self = this 或 _this = this 或.bind(this)。例如恨统,下面的代碼用ES5就不是很優(yōu)雅:
var _this = this;
$('.btn').click(function(event){
? _this.sendData();
})
在ES6中就不需要用 _this = this:
$('.btn').click((event) =>{
? this.sendData();
})
不幸的是叁扫,ES6委員會決定三妈,以前的function的傳遞方式也是一個很好的方案,所以它們?nèi)匀槐A袅艘郧暗墓δ堋?/p>
下面這是一個另外的例子莫绣,我們通過call傳遞文本給logUpperCase() 函數(shù)在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中=>可以混合和匹配老的函數(shù)一起使用模燥。當在一行代碼中用了箭頭函數(shù),它就變成了一個表達式掩宜。它將暗地里返回單個語句的結(jié)果蔫骂。如果你超過了一行,將需要明確使用return牺汤。
這是用ES5代碼創(chuàng)建一個消息數(shù)組:
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
請注意辽旋,這里用了字符串模板。
在箭頭函數(shù)中檐迟,對于單個參數(shù)补胚,括號()是可選的,但當你超過一個參數(shù)的時候你就需要他們追迟。
在ES5代碼有明確的返回功能:
var ids = ['5632953c4e345e145fdf2df8', '563295464e345e145fdf2df9'];
var messages = ids.map(function (value, index, list) {
? return 'ID of ' + index + ' element is ' + value + ' '; // explicit return
});
在ES6中有更加嚴謹?shù)陌姹救芷洌瑓?shù)需要被包含在括號里并且它是隱式的返回:
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 實現(xiàn)語法敦间。Q瓶逃,bluebird,deferred.js每瞒,vow, avow, jquery 一些可以列出名字的金闽。也有人說我們不需要promises,僅僅使用異步剿骨,生成器代芜,回調(diào)等就夠了。但令人高興的是浓利,在ES6中有標準的Promise實現(xiàn)挤庇。
下面是一個簡單的用setTimeout()實現(xiàn)的異步延遲加載函數(shù):
setTimeout(function(){
? console.log('Yay!');
}, 1000);
在ES6中,我們可以用promise重寫:
var wait1000 =? new Promise(function(resolve, reject) {
? setTimeout(resolve, 1000);
}).then(function() {
? console.log('Yay!');
});
或者用ES6的箭頭函數(shù):
var wait1000 =? new Promise((resolve, reject)=> {
? setTimeout(resolve, 1000);
}).then(()=> {
? console.log('Yay!');
});
到目前為止贷掖,代碼的行數(shù)從三行增加到五行嫡秕,并沒有任何明顯的好處。確實苹威,如果我們有更多的嵌套邏輯在setTimeout()回調(diào)函數(shù)中昆咽,我們將發(fā)現(xiàn)更多好處:
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 比普通回調(diào)更好?其實我也不確信,我認為一旦你有回調(diào)的想法掷酗,那么就沒有必要額外增加promises的復雜性调违。
雖然,ES6 有讓人崇拜的Promises 泻轰。Promises 是一個有利有弊的回調(diào)但是確實是一個好的特性技肩,更多詳細的信息關于promise:Introduction to ES6 Promises.
8.Block-Scoped Constructs Let and Const(塊作用域和構(gòu)造let和const)**
在ES6代碼中,你可能已經(jīng)看到那熟悉的身影l(fā)et浮声。在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));
結(jié)果將返回1000玷过,這真是一個bug。在ES6中筑煮,我們用let限制塊級作用域。而var是限制函數(shù)作用域粤蝎。
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));
這個結(jié)果將會是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使這個語言變復雜了贞间。沒有它們的話贿条,我們只需考慮一種方式,現(xiàn)在有許多種場景需要考慮增热。
9. Classes (類)in ES6
如果你喜歡面向?qū)ο缶幊蹋∣OP)整以,那么你將喜愛這個特性。以后寫一個類和繼承將變得跟在facebook上寫一個評論那么容易峻仇。
類的創(chuàng)建和使用真是一件令人頭疼的事情在過去的ES5中公黑,因為沒有一個關鍵字class (它被保留,但是什么也不能做)。在此之上凡蚜,大量的繼承模型像pseudo classical, classical, functional 更加增加了混亂奠骄,JavaScript 之間的宗教戰(zhàn)爭只會更加火上澆油。
用ES5寫一個類番刊,有很多種方法含鳞,這里就先不說了。現(xiàn)在就來看看如何用ES6寫一個類吧芹务。ES6沒有用函數(shù), 而是使用原型實現(xiàn)類蝉绷。我們創(chuàng)建一個類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使用了默認參數(shù)值熔吗。此外方法名也不需要加function關鍵字,而且冒號(:)也不需要了佳晶。另外一個大的區(qū)別就是你不需要分配屬性this∥荩現(xiàn)在設置一個屬性的值,只需簡單的在構(gòu)造函數(shù)中分配轿秧。
AccountModel 從類baseModel 中繼承而來:
class AccountModel extends baseModel {
? ? constructor(options, data) {
為了調(diào)用父級構(gòu)造函數(shù)中跌,可以毫不費力的喚起super()用參數(shù)傳遞:
? ? ? 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;
? ? }
}
那么菇篡,你如何調(diào)用他們呢漩符?它是非常容易的:
let accounts = new AccountModel(5);
accounts.getName();
console.log('Data is %s', accounts.accountsData);
結(jié)果令人驚訝,輸出是:
Class name: Account Model
Data is? 32113123123,524214691
10. Modules (模塊)in ES6
眾所周知驱还,在ES6以前JavaScript并不支持本地的模塊嗜暴。人們想出了AMD,RequireJS议蟆,CommonJS以及其它解決方法∶屏ぃ現(xiàn)在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已經(jīng)敲定耐薯,但并不是所有的瀏覽器都完全支持,詳見:http://kangax.github.io/compat-table/es6/丝里。要使用ES6曲初,需要一個編譯器例如:babel。你可以把它作為一個獨立的工具使用杯聚,也可以把它放在構(gòu)建中臼婆。grunt,gulp和webpack中都有可以支持babel的插件幌绍。
6941baebjw1f61bokq3xbj20yb0pdq6a
這是一個gulp案列颁褂,安裝gulp-babel插件:
$ npm install --save-dev gulp-babel
在gulpfile.js中,定義一個任務build傀广,放入src/app.js颁独,并且編譯它進入構(gòu)建文件中。
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中主儡,你可以用構(gòu)建工具或者獨立的Babel模塊 babel-core 來編譯你的Node.js文件奖唯。安裝如下:
$ npm install --save-dev babel-core
然后在node.js中,你可以調(diào)用這個函數(shù):
require("babel-core").transform(ES5Code, options);
ES6總結(jié)
這里還有許多ES6的其它特性你可能會使用到糜值,排名不分先后:
1、全新的Math, Number, String, Array 和 Object 方法
2坯墨、二進制和八進制數(shù)據(jù)類型
3寂汇、默認參數(shù)不定參數(shù)擴展運算符
4、Symbols符號
5捣染、tail調(diào)用
6骄瓣、Generators (生成器)
7、New data structures like Map and Set(新的數(shù)據(jù)構(gòu)造對像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:立刻運行的函數(shù)表達式
中直接寫可以運行的代碼(簡稱IIFE)耍攘,或者一些庫像AMD榕栏。然而在ES6中,你可以用export導入你的類蕾各。下面舉個例子扒磁,在ES5中,module.js有port變量和getAccounts 方法: