原博:
前端開發(fā)者不得不知的 JavaScript ES6 十大特性
轉(zhuǎn)載自AlloyTeam:
http://www.alloyteam.com/2016/03/es6-front-end-developers-will-have-to-know-the-top-ten-properties/
ES6(ECMAScript2015)的出現(xiàn)箱硕,無疑給前端開發(fā)人員帶來了新的驚喜氧映,它包含了一些很棒的新特性歌径,可以更加方便的實現(xiàn)很多復(fù)雜的操作,提高開發(fā)人員的效率锣笨。
本文主要針對ES6做一個簡要介紹。 主要譯自:
http://webapplog.com/ES6/comment-page-1/唆涝。也許你還不知道ES6是什么, 實際上, 它是一種新的javascript規(guī)范审残。在這個大家都很忙碌的時代,如果你想對ES6有一個快速的了解酥夭,那么請繼續(xù)往下讀,去了解當(dāng)今最流行的編程語言JavaScript最新一代的十大特性脊奋。
以下是ES6排名前十的最佳特性列表(排名不分先后):
- Default Parameters(默認(rèn)參數(shù)) in ES6
- Template Literals (模板文本)in ES6
- Multi-line Strings (多行字符串)in ES6
- Destructuring Assignment (解構(gòu)賦值)in ES6
- Enhanced Object Literals (增強(qiáng)的對象文本)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標(biāo)準(zhǔn)確立。
3脉执、1999:ES3出現(xiàn)疼阔,與此同時IE5風(fēng)靡一時。
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標(biāo)準(zhǔn)。
6贴浙、2015:ES6/ECMAScript2015出現(xiàn)砂吞。
歷史回顧就先到此,現(xiàn)讓我們進(jìn)入正題崎溃。
1.Default Parameters(默認(rèn)參數(shù)) in ES6
還記得我們以前不得不通過下面方式來定義默認(rèn)參數(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,它是默認(rèn)被hard-coded的值囱修,而不能變成參數(shù)本身的值赎瑰。當(dāng)然,如果你非要用0作為值破镰,我們可以忽略這一缺陷并且使用邏輯OR就行了餐曼!但在ES6,我們可以直接把默認(rèn)值放在函數(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');
我們可能需要一些時間來習(xí)慣解構(gòu)賦值語法的使用,但是它確實能給我們帶來許多意外的收獲泄伪。
5.Enhanced Object Literals (增強(qiáng)的對象字面量)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對象中將有下面這些屬性:
為了方便舉例,我們將考慮它們的相似處尔苦。所以在ES6的對象文本中涩馆,既可以直接分配getAccounts: getAccounts,也可以只需用一個getAccounts,此外允坚,我們在這里通過proto(并不是通過’proto’)設(shè)置屬性魂那,如下所示:
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)
ES6對象文本是一個很大的進(jìn)步對于舊版的對象文本來說涯雅。
6.Arrow Functions in(箭頭函數(shù)) ES6
這是我迫不及待想講的一個特征,CoffeeScript 就是因為它豐富的箭頭函數(shù)讓很多開發(fā)者喜愛展运。在ES6中活逆,也有了豐富的箭頭函數(shù)。這些豐富的箭頭是令人驚訝的因為它們將使許多操作變成現(xiàn)實拗胜,比如蔗候,
以前我們使用閉包,this總是預(yù)期之外地產(chǎn)生改變挤土,而箭頭函數(shù)的迷人之處在于,現(xiàn)在你的this可以按照你的預(yù)期使用了误算,身處箭頭函數(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袅艘郧暗墓δ堋?br> 下面這是一個另外的例子,我們通過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ù)一起使用伍绳。當(dāng)在一行代碼中用了箭頭函數(shù)踊挠,它就變成了一個表達(dá)式。它將暗地里返回單個語句的結(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ù)旺芽,括號()是可選的沪猴,但當(dāng)你超過一個參數(shù)的時候你就需要他們。
在ES5代碼有明確的返回功能:
var ids = ['5632953c4e345e145fdf2df8', '563295464e345e145fdf2df9'];
var messages = ids.map(function (value, index, list) {
return 'ID of ' + index + ' element is ' + value + ' '; // explicit return
});
在ES6中有更加嚴(yán)謹(jǐn)?shù)陌姹旧蹋瑓?shù)需要被包含在括號里并且它是隱式的返回:
var ids = ['5632953c4e345e145fdf2df8','563295464e345e145fdf2df9'];
var messages = ids.map((value, index, list) => `ID of ${index} element is ${value} `); // implicit return
- Promises in ES6
Promises 是一個有爭議的話題字币。因此有許多略微不同的promise 實現(xiàn)語法。Q共缕,bluebird洗出,deferred.js,vow, avow, jquery 一些可以列出名字的图谷。也有人說我們不需要promises翩活,僅僅使用異步,生成器便贵,回調(diào)等就夠了菠镇。但令人高興的是,在ES6中有標(biāo)準(zhǔn)的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)更好轴猎?其實我也不確信,我認(rèn)為一旦你有回調(diào)的想法进萄,那么就沒有必要額外增加promises的復(fù)雜性捻脖。
雖然锐峭,ES6 有讓人崇拜的Promises 。Promises 是一個有利有弊的回調(diào)但是確實是一個好的特性可婶,更多詳細(xì)的信息關(guān)于promise:Introduction to ES6 Promises.
8.Block-Scoped Constructs Let and Const(塊作用域和構(gòu)造let和const)
在ES6代碼中沿癞,你可能已經(jīng)看到那熟悉的身影l(fā)et。在ES6里let并不是一個花俏的特性扰肌,它是更復(fù)雜的抛寝。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).那么這個表達(dá)式將返回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使這個語言變復(fù)雜了冒签。沒有它們的話,我們只需考慮一種方式钟病,現(xiàn)在有許多種場景需要考慮萧恕。
9. Classes (類)in ES6
如果你喜歡面向?qū)ο缶幊蹋∣OP),那么你將喜愛這個特性肠阱。以后寫一個類和繼承將變得跟在facebook上寫一個評論那么容易票唆。
類的創(chuàng)建和使用真是一件令人頭疼的事情在過去的ES5中,因為沒有一個關(guān)鍵字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使用了默認(rèn)參數(shù)值肆糕。此外方法名也不需要加function關(guān)鍵字般堆,而且冒號(:)也不需要了。另外一個大的區(qū)別就是你不需要分配屬性this〕峡校現(xiàn)在設(shè)置一個屬性的值淮摔,只需簡單的在構(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 設(shè)置成一個屬性:
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
- Modules (模塊)in ES6
眾所周知,在ES6以前JavaScript并不支持本地的模塊五辽。人們想出了AMD办斑,RequireJS,CommonJS以及其它解決方法”计辏現(xiàn)在ES6中可以用模塊import 和export 操作了俄周。
在ES5中,你可以在 <script>中直接寫可以運行的代碼(簡稱IIFE)髓迎,或者一些庫像AMD峦朗。然而在ES6中,你可以用export導(dǎo)入你的類排龄。下面舉個例子波势,在ES5中,module.js有port變量和getAccounts 方法:
module.exports = {
port: 3000,
getAccounts: function() {
...
}
}
在ES5中,main.js需要依賴require('module') 導(dǎo)入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來導(dǎo)入到文件main.js中争舞,我們需用import {name} from 'my-module'語法凛忿,例如:
import {port, getAccounts} from 'module';
console.log(port); // 3000
或者我們可以在main.js中把整個模塊導(dǎo)入, 并命名為 service:
import * as service from 'module';
console.log(service.port); // 3000
從我個人角度來說,我覺得ES6模塊是讓人困惑的竞川。但可以肯定的事店溢,它們使語言更加靈活了叁熔。
并不是所有的瀏覽器都支持ES6模塊,所以你需要使用一些像jspm去支持ES6模塊床牧。
更多的信息和例子關(guān)于ES6模塊荣回,請看 this text。不管怎樣戈咳,請寫模塊化的JavaScript心软。
如何使用ES6 (Babel)
ES6已經(jīng)敲定,但并不是所有的瀏覽器都完全支持著蛙,詳見:http://kangax.github.io/compat-table/es6/斥扛。要使用ES6本慕,需要一個編譯器例如:babel规揪。你可以把它作為一個獨立的工具使用度苔,也可以把它放在構(gòu)建中。grunt暂吉,gulp和webpack中都有可以支持babel的插件胖秒。
這是一個gulp案列,安裝gulp-babel插件:
$ npm install --save-dev gulp-babel
在gulpfile.js中慕的,定義一個任務(wù)build阎肝,放入src/app.js,并且編譯它進(jìn)入構(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绕辖、二進(jìn)制和八進(jìn)制數(shù)據(jù)類型
3摇肌、默認(rèn)參數(shù)不定參數(shù)擴(kuò)展運算符
4、Symbols符號
5仪际、tail調(diào)用
6围小、Generators (生成器)
7、New data structures like Map and Set(新的數(shù)據(jù)構(gòu)造對像MAP和set)
參考文獻(xiàn):
- ES6 Cheatsheet (FREE PDF)
- http://webapplog.com/ES6/comment-page-1/
- Understanding ECMAScript 6?by Nicolas Zakas book
- http://ES6-features.org/#DateTimeFormatting
- IIFE:立刻運行的函數(shù)表達(dá)式