1. 程式人生 > >深入ES6(一)歷史與簡介

深入ES6(一)歷史與簡介

第一章 歷史與簡介

ES6於2015年6月正式釋出,其目標是使JS原因呢可用於編寫大型的應用程式,成為企業級的開發語言。這篇文章主要介紹:
1. ECMAScript的版本歷史
2. 使用babel對ES6程式碼進行轉碼
3. 配合gulp搭建ES6開發環境
4. ES6最常用的特性

1.1 ES6與ES2015

標準的制定者計劃,以後每年釋出一次標準,使用年份作為版本號。ES6是在2015年釋出的,所以又稱為ES2015。但ES6其實多用於泛指ES5.1版本後的下一代JS標準,它涵蓋在ES2015版本上稍作改動的ES2016。

1.2 ECMAScript與Javascript

1996年11月,javascript的創造者網景公司將JS提交給國際化標準組織ECMA(European computer manufactures association,歐洲計算機制造聯合會),希望這種語言能夠成為國際標準。次年,ECMA釋出了規定瀏覽器指令碼語言的標準,即ECMAScript。該標準一開始就是針對JS語言制定的,之所以不使用Javascript這個名號,一是因為商標使用權的衝突,二則是想體現這門語言的制定者是ECMA而並非網景,這樣有利於這門語言的開放和中立。

因此,可以這麼說,ECMAScript是標準與規格,Javascript是對它的實現。此外,像Flash的ActionScript也遵循ECMAScript的規範,所以其基本語法看起來和javascript幾乎一模一樣。二者的主要區別則在於對頁面元素的操作。使用JS可以輕鬆的對頁面的元素進行操作,與瀏覽器進行互動,所以很多人又把javascript看成是ECMAScript+DOM(文件物件模型)+BOM(瀏覽器物件模型)的組合。

1.3 ECMAScript的歷史

  1. 1997年 ECMAScript 1.0 誕生
  2. 1999年12月 ECMAScript 3.0誕生,它 是一個巨大的成功,在業界得到了廣泛的支援,它奠定了JS的基本語法,被其後版本完全繼承。直到今天,我們一開始學習JS,其實就是在學3.0版的語法。
  3. 2000年的ECMAScript4.0是當下ES6的前身,但由於這個版本太過激烈,對ES3做了徹底升級,所以暫時被“和諧”了。
  4. 2009年12月,ECMAScript5.0版正式釋出。ECMA專家組預計ECMAScript的第五個版本會在2013年中期到2018年作為主流的開發標準。2011年6月,ES5.1版釋出,並且成為ISO國際標準。
  5. 2013年,ES6草案凍結,不再新增新的功能,新的功能將被放到ES7中;2015年6月,ES6正式通過,成為國際標準。

1.4 轉碼

ES6強大的語法能夠大大的加快我們的開發效率,但缺點在於老舊的環境對它的支援並不理想。我們可以使用轉碼技術將ES6程式碼轉為被瀏覽器普遍支援的ES5程式碼,以保證瀏覽器的支援。Babel是當下最流行的轉碼器。

babel的名字取自“巴別塔”,據《聖經·舊約·創世記》第11章記載,是當時人類聯合起來興建,希望能通往天堂的高塔。為了阻止人類的計劃,上帝讓人類說不同的語言,使人類相互之間不能溝通,計劃因此失敗,人類自此各散東西,向世界各地遷移。

babel命令列工具

babel的命令列通過下面程式碼來安裝

npm install babel-cli -g
npm install babel-preset-es2015 --save

然後再根目錄建立名為.babelrc的配置檔案。

{
    "presets" : ["es2015"]
}

babel提供下面幾種處理ES6程式碼的方式:

babel-node執行ES6程式碼

以REPL的方式直接執行ES6程式碼

babel-node
> var a = 10;console.log((a=>a*a)(a));
> 100

也可以直接執行ES6指令碼。比如現在我們有兩個ES6模組m1.js和m2.js,m1.js的內容如下:

import myInfo from './m2.js';
myInfo();

m2.js的內容為:

export default function () {
    console.log('default')
};

執行

babel-node m1.js

輸出:default。可見兩個ES6檔案都被正確的解析執行了。

Babel轉換ES6程式碼

使用babel命令可以直接把ES6程式碼轉化成ES5程式碼,但預設只是把轉換後的程式碼輸出到控制檯,我們可以加上-o命令將轉換後的程式碼重定向至檔案。

babel foo.js -o bar.js

-d引數可以轉換整個目錄,注意資源目錄和構建目錄的順序

babel -d build-dir sourse-dir

如果希望生成sourse-map,需要加上-s引數

babel foo.js -o bar.js -s

Babel在瀏覽器環境

首先在瀏覽器引入browser.js,然後在type=”text/babel”的script元素中編寫你的程式碼就可以了。可以使用下面的命令從npm上獲取browser.js

npm install [email protected]

檔案在node_modules/babel-core子目錄下。因為從babel6.0開始,不再直接提供瀏覽器的版本,所以我們通過安裝第5個版本獲取。

顯然,這種做法對瀏覽器效能會有很壞的影響,而且本質上講,這沒有什麼用。在開發時,最新版本的chrome可以實現大部分的ES6特性(除了模組的特性),在上線時,則可以使用已經經過轉碼的版本。

Babel在Nodejs環境

首先需要安裝核心模組

npm install babel-core
npm install babel-preset-es2015 --save

然後再根目錄建立名為.babelrc的配置檔案。

{
    "presets" : ["es2015"]
}

然後在應用的指令碼入口(比如express的app.js)加上如下語句:

require("babel-core/register")

這樣,後面所有通過require命令載入字尾名為.es6.js.jsx.es的指令碼,都會通過babel先轉碼,再載入。

Babel不會轉換Iterator、Generator、Set、Map等全域性物件以及一些定義在全域性物件上的方法,如果用到了這些功能,你的環境卻不支援,就需要安裝使用babel-polyfill模組。

npm install babel-polyfill --save

然後在所有指令碼頭部加上

require('babel-polyfill');

還可以使用babel-core模組以編碼的形式進行轉碼,這可能會呼叫到nodejs的檔案系統。我們一般使用gulp等構建工具取完成這個功能。

1.5 使用gulp搭建ES6環境

開發中,我們常常配合gulp來搭建ES6開發環境。

下載依賴模組

  "devDependencies": {
    "babel-preset-es2015": "^6.24.0",
    "gulp": "^3.9.1",
    "gulp-babel": "^6.1.2",
    "gulp-plumber": "^1.1.0",
    "gulp-rename": "^1.2.2",
    "gulp-sequence": "^0.4.6",
    "gulp-watch": "^4.3.11"
  }

需要babel-preset-es2015和gulp-babel轉換ES6程式碼為ES5。

gulp-plumber的作用是在出現錯誤時重新啟動gulp(gulp的設計使得gulp非常容易由於你想不到的原因掛掉,比如你使用let重複命名了變數)。

gulp-rename重新命名產生的ES5檔案,在後面加上“-es5”字尾。

gulp-watch用來檢測檔案變化,與gulp.watch()不同,gulp-watch可以監聽檔案的增加與刪除。gulp-sequence與gulp-watch聯合使用,它的作用是順序觸發一些任務。當watch監聽到檔案變化時(改變或增加),可以使用它觸發任務。

配置檔案

glupfile.js如下所示:

"use strict";

let gulp = require('gulp');
let babel = require("gulp-babel");
let rename = require("gulp-rename");
let plumber = require("gulp-plumber");
let watch = require("gulp-watch");
let gulpSequence  = require("gulp-sequence");
let del = require('del');

gulp.task("es6", function () {
    return gulp.src(['./project/**/*.js', '!./project/**/*-es5.js'])
    .pipe(plumber())
    .pipe(babel({
        presets: ['es2015'],
        compact: false
    }))
    .pipe(rename({
        suffix: "-es5",
        extname: ".js"
    }))
    .pipe(gulp.dest("./project"));
});


gulp.task('default', ['es6'], function () {
    watch('./project/**/*.js', function () {
        gulpSequence('es6')(function (err) {
            if (err) console.log(err)
        })
    });

    watch('./project/**/*.js', {events:['unlink']}, function (vinyl) {
        let filename = vinyl.path.replace('.js','-es5.js');
        del(filename)
    });
});

這裡我新加了一個功能,如果刪除了es6檔案,其生產的es5檔案也對應被刪除。如果不想這樣,把上面這部分程式碼(del watch unlink那部分)註釋掉就可以了。
還有一個比較坑的地方,就是在配置src路徑的時候,如果不指定一個初始資料夾,比如./project,只寫成./這樣,就會報一些很奇怪的錯誤,回來有機會在解決。

1.6 ES6常用語法簡介

這一節我們將會對接下來的教程中,我們將用到的取代ES5中的傳統編碼方法的高效ES6方法進行簡要的介紹。

let與const

使用let代替var,let較var而言,支援塊級作用域,不存在程式碼提升,同一作用域內不允許出現相同名稱的變數。const與let特性基本相同,我們使用const宣告常量,一旦宣告必須立即賦值,並且不可更改。

箭頭函式與函式預設值

使用箭頭函式可以簡化簡單邏輯函式的編碼,例如:

var foo = function(a){
    a = a||1; // a的預設值為1
    return a*a
}

可以用箭頭函式+引數預設值表示為:

let foo = (a=1)=>a*a
//更完全的形式為
let foo = (a=1)=>{return a*a}

模板字串

使用模板字串可以輕鬆的在字串中進行變數的替換。注意使用模板時,最外層為“號(反引號),變數使用${}包裹。

let name = 'Kyle';
let bar ='學生'+name+'正在學習ES6';
// 等價於
let foo = `學生${name}正在學習ES6`;

解構賦值

解構賦值可以幫助我們快速的把物件裡的值轉移到區域性變數中,比如:

let {c, d} = {c:'c',d:'d',e:'e'};
console.log(c); // c
console.log(d); // d

let [d,b,c] = [1,2,3];
console.log(d); // 1
console.log(b); //2
console.log(c); //3

模組化

ES6提供了模組化的原生支援

// 引入模組
import {readFile, writeFile} from 'fs'
readFile('./a.txt', ()=>{
    // do something
});
// 匯出模組
export function foo(){
    console.log('foo');
}

其它

ES6還提供了其它非常有用的語法與特性,比如:class作為傳進物件的語法糖,promise提供非同步呼叫的解決方案,Set,Map資料結構等等。在接下來的幾天內我們將一點點深入學習。