ECMAScript 6 介紹

2021-09-15 16:17 更新

概述

ECMAScript 6 是JavaScript的下一代標(biāo)準(zhǔn),正處在快速開發(fā)之中,大部分已經(jīng)完成了,預(yù)計將在2014年正式發(fā)布。Mozilla將在這個標(biāo)準(zhǔn)的基礎(chǔ)上,推出JavaScript 2.0。

ECMAScript 6的目標(biāo),是使得JavaScript可以用來編寫復(fù)雜的應(yīng)用程序、函數(shù)庫和代碼的自動生成器(code generator)。

最新的瀏覽器已經(jīng)部分支持ECMAScript 6 的語法,可以通過《ECMAScript 6 瀏覽器兼容表》查看瀏覽器支持情況。

下面對ECMAScript 6新增的語法特性逐一介紹。由于ECMAScript 6的正式標(biāo)準(zhǔn)還未出臺,所以以下內(nèi)容隨時可能發(fā)生變化,不一定是最后的版本。

使用ECMAScript 6的方法

目前,V8引擎已經(jīng)部署了ECMAScript 6的部分特性。使用node.js 0.11版,就可以體驗這些特性。

node.js 0.11版的一種比較方便的使用方法,是使用版本管理工具nvm。下載nvm以后,進入項目目錄,運行下面的命令,激活nvm。

source nvm.sh

然后,指定node運行版本。

nvm use 0.11

最后,用--harmony參數(shù)進入node運行環(huán)境,就可以在命令行下體驗ECMAScript 6了。

node --harmony

另外,可以使用Google的Traceur在線轉(zhuǎn)換工具),將ES6代碼編譯為ES5。

# 安裝
npm install -g traceur

# 運行ES6文件
traceur /path/to/es6

# 將ES6文件轉(zhuǎn)為ES5文件
traceur --script /path/to/es6 --out /path/to/es5

數(shù)據(jù)類型

let命令

(1)概述

ECMAScript 6新增了let命令,用來聲明變量。它的用法類似于var,但是所聲明的變量,只在let命令所在的代碼塊內(nèi)有效。

{
    let a = 10;
    var b = 1;
}

a // ReferenceError: a is not defined. 
b //1

上面代碼在代碼塊之中,分別用let和var聲明了兩個變量。然后在代碼塊之外調(diào)用這兩個變量,結(jié)果let聲明的變量報錯,var聲明的變量返回了正確的值。這表明,let聲明的變量只在它所在的代碼塊有效。

下面的代碼如果使用var,最后輸出的是9。

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

如果使用let,聲明的變量僅在塊級作用域內(nèi)有效,最后輸出的是6。

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

注意,let不允許在相同作用域內(nèi),重復(fù)聲明同一個變量。

// 報錯
{
    let a = 10;
    var a = 1;
}

// 報錯
{
    let a = 10;
    let a = 1;
}

(2)塊級作用域

let實際上為JavaScript新增了塊級作用域。

function f1() {
  let n = 5;
  if (true) {
      let n = 10;
  }
  console.log(n); // 5
}

上面的函數(shù)有兩個代碼塊,都聲明了變量n,運行后輸出5。這表示外層代碼塊不受內(nèi)層代碼塊的影響。如果使用var定義變量n,最后輸出的值就是10。

塊級作用域的出現(xiàn),實際上使得獲得廣泛應(yīng)用的立即執(zhí)行函數(shù)(IIFE)不再必要了。

// IIFE寫法
(function () { 
    var tmp = ...;
    ...
}()); 

// 塊級作用域?qū)懛?{
    let tmp = ...;
    ...
}

(3)不存在變量提升

需要注意的是,let聲明的變量不存在“變量提升”現(xiàn)象。

console.log(x);
let x = 10;

上面代碼運行后會報錯,表示x沒有定義。如果用var聲明x,就不會報錯,輸出結(jié)果為undefined。

const命令

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

const PI = 3.1415;

PI
// 3.1415

PI = 3;

PI
// 3.1415

const PI = 3.1;

PI
// 3.1415

上面代碼表明改變常量的值是不起作用的。需要注意的是,對常量重新賦值不會報錯,只會默默地失敗。

const的作用域與var命令相同:如果在全局環(huán)境聲明,常量就在全局環(huán)境有效;如果在函數(shù)內(nèi)聲明,常量就在函數(shù)體內(nèi)有效。

Set數(shù)據(jù)結(jié)構(gòu)

ES6提供了新的數(shù)據(jù)結(jié)構(gòu)Set。它類似于數(shù)組,但是成員的值都是唯一的,沒有重復(fù)的值。

Set本身是一個構(gòu)造函數(shù),用來生成Set數(shù)據(jù)結(jié)構(gòu)。

var s = new Set();

[2,3,5,4,5,2,2].map(x => s.add(x))
for (i of s) {console.log(i)}
// 2 3 4 5

上面代碼表示,set數(shù)據(jù)結(jié)構(gòu)不會添加重復(fù)的值。

set數(shù)據(jù)結(jié)構(gòu)有以下屬性和方法:

  • size:返回成員總數(shù)。
  • add(value):添加某個值。
  • delete(value):刪除某個值。
  • has(value):返回一個布爾值,表示該值是否為set的成員。
  • clear():清除所有成員。
s.add("1").add("2").add("2"); 
// 注意“2”被加入了兩次

s.size // 2

s.has("1")    // true
s.has("2")    // true
s.has("3")   // false

s.delete("2");
s.has("2")    // false

Map數(shù)據(jù)結(jié)構(gòu)

ES6還提供了map數(shù)據(jù)結(jié)構(gòu)。它類似于對象,就是一個鍵值對的集合,但是“鍵”的范圍不限于字符串,甚至對象也可以當(dāng)作鍵。

var m = new Map();

o = {p: "Hello World"};
m.set(o, "content")
console.log(m.get(o))
// "content"

上面代碼將一個對象當(dāng)作m的一個鍵。

Map數(shù)據(jù)結(jié)構(gòu)有以下屬性和方法。

  • size:返回成員總數(shù)。
  • set(key, value):設(shè)置一個鍵值對。
  • get(key):讀取一個鍵。
  • has(key):返回一個布爾值,表示某個鍵是否在Map數(shù)據(jù)結(jié)構(gòu)中。
  • delete(key):刪除某個鍵。
  • clear():清除所有成員。
var m = new Map(); 

m.set("edition", 6)        // 鍵是字符串
m.set(262, "standard")     // 鍵是數(shù)值
m.set(undefined, "nah")    // 鍵是undefined

var hello = function() {console.log("hello");}
m.set(hello, "Hello ES6!") // 鍵是函數(shù)

m.has("edition")     // true
m.has("years")       // false
m.has(262)           // true
m.has(undefined)     // true
m.has(hello)         // true

m.delete(undefined)
m.has(undefined)       // false

m.get(hello)  // Hello ES6!
m.get("edition")  // 6

rest(...)運算符

(1)基本用法

ES6引入rest運算符(...),用于獲取函數(shù)的多余參數(shù),這樣就不需要使用arguments.length了。rest運算符后面是一個數(shù)組變量,該變量將多余的參數(shù)放入數(shù)組中。

function add(...values) {
   let sum = 0;

   for (var val of values) {
      sum += val;
   }

   return sum;
}

add(2, 5, 3) // 10

上面代碼的add函數(shù)是一個求和函數(shù),利用rest運算符,可以向該函數(shù)傳入任意數(shù)目的參數(shù)。

下面是一個利用rest運算符改寫數(shù)組push方法的例子。

function push(array, ...items) { 
  items.forEach(function(item) {
    array.push(item);
    console.log(item);
  });
}

var a = [];
push(a, "a1", "a2", "a3", "a4");

(2)將數(shù)組轉(zhuǎn)為參數(shù)序列

rest運算符不僅可以用于函數(shù)定義,還可以用于函數(shù)調(diào)用。

function f(s1, s2, s3, s4, s5) {
    console.log(s1 + s2 + s3 + s4 +s5);
}

var a = ["a2", "a3", "a4", "a5"];

f("a1", ...a)
// a1a2a3a4a5

從上面的例子可以看出,rest運算符的另一個重要作用是,可以將數(shù)組轉(zhuǎn)變成正常的參數(shù)序列。利用這一點,可以簡化求出一個數(shù)組最大元素的寫法。

// ES5
Math.max.apply(null, [14, 3, 77])

// ES6
Math.max(...[14, 3, 77])

// 等同于
Math.max(14, 3, 77);

上面代碼表示,由于JavaScript不提供求數(shù)組最大元素的函數(shù),所以只能套用Math.max函數(shù),將數(shù)組轉(zhuǎn)為一個參數(shù)序列,然后求最大值。有了rest運算符以后,就可以直接用Math.max了。

遍歷器(Iterator)

遍歷器(Iterator)是一種協(xié)議,任何對象都可以部署遍歷器協(xié)議,從而使得for...of循環(huán)可以遍歷這個對象。

遍歷器協(xié)議規(guī)定,任意對象只要部署了next方法,就可以作為遍歷器,但是next方法必須返回一個包含value和done兩個屬性的對象。其中,value屬性當(dāng)前遍歷位置的值,done屬性是一個布爾值,表示遍歷是否結(jié)束。

function makeIterator(array){
    var nextIndex = 0;

    return {
       next: function(){
           return nextIndex < array.length ?
               {value: array[nextIndex++], done: false} :
               {done: true};
       }
    }
}

var it = makeIterator(['a', 'b']);

it.next().value // 'a'
it.next().value // 'b'
it.next().done  // true

下面是一個無限運行的遍歷器的例子。

function idMaker(){
    var index = 0;

    return {
       next: function(){
           return {value: index++, done: false};
       }
    }
}

var it = idMaker();

it.next().value // '0'
it.next().value // '1'
it.next().value // '2'
// ...

generator 函數(shù)

上一部分的遍歷器,用來依次取出集合中的每一個成員,但是某些情況下,我們需要的是一個內(nèi)部狀態(tài)的遍歷器。也就是說,每調(diào)用一次遍歷器,對象的內(nèi)部狀態(tài)發(fā)生一次改變(可以理解成發(fā)生某些事件)。ECMAScript 6 引入了generator函數(shù),作用就是返回一個內(nèi)部狀態(tài)的遍歷器,主要特征是函數(shù)內(nèi)部使用了yield語句。

當(dāng)調(diào)用generator函數(shù)的時候,該函數(shù)并不執(zhí)行,而是返回一個遍歷器(可以理解成暫停執(zhí)行)。以后,每次調(diào)用這個遍歷器的next方法,就從函數(shù)體的頭部或者上一次停下來的地方開始執(zhí)行(可以理解成恢復(fù)執(zhí)行),直到遇到下一個yield語句為止,并返回該yield語句的值。

ECMAScript 6草案定義的generator函數(shù),需要在function關(guān)鍵字后面,加一個星號。然后,函數(shù)內(nèi)部使用yield語句,定義遍歷器的每個成員。

function* helloWorldGenerator() {
    yield 'hello';
    yield 'world';
}

yield有點類似于return語句,都能返回一個值。區(qū)別在于每次遇到y(tǒng)ield,函數(shù)返回緊跟在yield后面的那個表達式的值,然后暫停執(zhí)行,下一次從該位置繼續(xù)向后執(zhí)行,而return語句不具備位置記憶的功能。

上面代碼定義了一個generator函數(shù)helloWorldGenerator,它的遍歷器有兩個成員“hello”和“world”。調(diào)用這個函數(shù),就會得到遍歷器。

var hw = helloWorldGenerator();

執(zhí)行遍歷器的next方法,則會依次遍歷每個成員。

hw.next() 
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: undefined, done: true }

hw.next()
// Error: Generator has already finished
// at GeneratorFunctionPrototype.next (native)
// at repl:1:3
//  at REPLServer.defaultEval (repl.js:129:27)
//  ...

上面代碼一共調(diào)用了四次next方法。

  • 第一次調(diào)用:函數(shù)開始執(zhí)行,直到遇到第一句yield語句為止。next方法返回一個對象,它的value屬性就是當(dāng)前yield語句的值hello,done屬性的值false,表示遍歷還沒有結(jié)束。

  • 第二次調(diào)用:函數(shù)從上次yield語句停下的地方,一直執(zhí)行到下一個yield語句。next方法返回一個對象,它的value屬性就是當(dāng)前yield語句的值world,done屬性的值false,表示遍歷還沒有結(jié)束。

  • 第三次調(diào)用:函數(shù)從上次yield語句停下的地方,一直執(zhí)行到函數(shù)結(jié)束。next方法返回一個對象,它的value屬性就是函數(shù)最后的返回值,由于上例的函數(shù)沒有return語句(即沒有返回值),所以value屬性的值為undefined,done屬性的值true,表示遍歷已經(jīng)結(jié)束。

  • 第四次調(diào)用:由于此時函數(shù)已經(jīng)運行完畢,next方法直接拋出一個錯誤。

遍歷器的本質(zhì),其實是使用yield語句暫停執(zhí)行它后面的操作,當(dāng)調(diào)用next方法時,再繼續(xù)往下執(zhí)行,直到遇到下一個yield語句,并返回該語句的值,如果直到運行結(jié)束。

如果next方法帶一個參數(shù),該參數(shù)就會被當(dāng)作上一個yield語句的返回值。

function* f() {
  for(var i=0; true; i++) {
    var reset = yield i;
    if(reset) { i = -1; }
  }
}

var g = f();

g.next() // { value: 0, done: false }
g.next() // { value: 1, done: false }
g.next(true) // { value: 0, done: false }

上面代碼先定義了一個可以無限運行的generator函數(shù)f,如果next方法沒有參數(shù),正常情況下返回一個遞增的i;如果next方法有參數(shù),則上一次yield語句的返回值將會等于該參數(shù)。如果該參數(shù)為true,則會重置i的值。

generator函數(shù)的這種暫停執(zhí)行的效果,意味著可以把異步操作寫在yield語句里面,等到調(diào)用next方法時再往后執(zhí)行。這實際上等同于不需要寫回調(diào)函數(shù)了,因為異步操作的后續(xù)操作可以放在yield語句下面,反正要等到調(diào)用next方法時再執(zhí)行。所以,generator函數(shù)的一個重要實際意義就是用來處理異步操作,改寫回調(diào)函數(shù)。

function* loadUI() { 
    showLoadingScreen(); 
    yield loadUIDataAsynchronously(); 
    hideLoadingScreen(); 
}

上面代碼表示,第一次調(diào)用loadUI函數(shù)時,該函數(shù)不會執(zhí)行,僅返回一個遍歷器。下一次對該遍歷器調(diào)用next方法,則會顯示登錄窗口,并且異步加載數(shù)據(jù)。再一次使用next方法,則會隱藏登錄窗口??梢钥吹?,這種寫法的好處是所有登錄窗口的邏輯,都被封裝在一個函數(shù),按部就班非常清晰。

下面是一個利用generator函數(shù),實現(xiàn)斐波那契數(shù)列的例子。

function* fibonacci() {
    var previous = 0, current = 1; 
    while (true) { 
        var temp = previous; 
        previous = current; 
        current = temp + current; 
        yield current; 
    } 
} 

for (var i of fibonacci()) { 
    console.log(i); 
} 
// 1, 2, 3, 5, 8, 13, ...,

下面是利用for...of語句,對斐波那契數(shù)列的另一種實現(xiàn)。

function* fibonacci() {
    let [prev, curr] = [0, 1];
    for (;;) {
        [prev, curr] = [curr, prev + curr];
        yield curr;
    }
}

for (n of fibonacci()) {
    if (n > 1000) break;
    console.log(n);
}

從上面代碼可見,使用for...of語句時不需要使用next方法。

這里需要注意的是,yield語句運行的時候是同步運行,而不是異步運行(否則就失去了取代回調(diào)函數(shù)的設(shè)計目的了)。實際操作中,一般讓yield語句返回Promises對象。

var Q = require('q');

function delay(milliseconds) {
    var deferred = Q.defer();
    setTimeout(deferred.resolve, milliseconds);
    return deferred.promise;
}

function *f(){
    yield delay(100);
};

上面代碼yield語句返回的就是一個Promises對象。

如果有一系列任務(wù)需要全部完成后,才能進行下一步操作,yield語句后面可以跟一個數(shù)組。下面就是一個例子。

function *f() {
    var urls = [
        'http://example.com/',
        'http://twitter.com/',
        'http://bbc.co.uk/news/'
    ];
    var arrayOfPromises = urls.map(someOperation);

    var arrayOfResponses = yield arrayOfPromises;

    this.body = "Results";
    for (var i = 0; i < urls.length; i++) {
        this.body += '\n' + urls[i] + ' response length is '
              + arrayOfResponses[i].body.length;
    }
};

原生對象的擴展

ES6對JavaScript的原生對象,進行了擴展,提供了一系列新的屬性和方法。

Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false

Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2

"abcde".contains("cd") // true
"abc".repeat(3) // "abcabcabc"

Array.from(document.querySelectorAll('*')) // Returns a real Array
Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
[0, 0, 0].fill(7, 1) // [0,7,7]
[1,2,3].findIndex(x => x == 2) // 1
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys() // iterator 0, 1, 2
["a", "b", "c"].values() // iterator "a", "b", "c"

Object.assign(Point, { origin: new Point(0,0) })

語法糖

ECMAScript 6提供了很多JavaScript語法的便捷寫法。

二進制和八進制表示法

ES6提供了二進制和八進制數(shù)值的新的寫法,分別用前綴0b和0o表示。

0b111110111 === 503 // true
0o767 === 503 // true

增強的對象寫法

ES6允許直接寫入變量和函數(shù),作為對象的屬性和方法。這樣的書寫更加簡潔。

var Person = {
  name: '張三',
  //等同于birth: birth
  birth,
  // 等同于hello: function ()...
  hello() { console.log('我的名字是', this.name); }
};

箭頭函數(shù)(arrow)

(1)定義

ES6允許使用“箭頭”(=>)定義函數(shù)。

var f = v => v;

上面的箭頭函數(shù)等同于:

var f = function(v) {
    return v;
};

如果箭頭函數(shù)不需要參數(shù)或需要多個參數(shù),就使用一個圓括號代表參數(shù)部分。

var f = () => 5; 
// 等同于
var f = function (){ return 5 };

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
    return num1 + num2;
};

如果箭頭函數(shù)的代碼塊部分多于一條語句,就要使用大括號將它們括起來,并且使用return語句返回。

var sum = (num1, num2) => { return num1 + num2; }

由于大括號被解釋為代碼塊,所以如果箭頭函數(shù)直接返回一個對象,必須在對象外面加上括號。

var getTempItem = id => ({ id: id, name: "Temp" });

(2)實例:回調(diào)函數(shù)的簡化

箭頭函數(shù)的一個用處是簡化回調(diào)函數(shù)。

// 正常函數(shù)寫法
[1,2,3].map(function (x) {
  return x * x;
});

// 箭頭函數(shù)寫法
[1,2,3].map(x => x * x);

另一個例子是

// 正常函數(shù)寫法
var result = values.sort(function(a, b) {
    return a - b;
});

// 箭頭函數(shù)寫法
var result = values.sort((a, b) => a - b);

(3)注意點

箭頭函數(shù)有幾個使用注意點。

  • 函數(shù)體內(nèi)的this對象,綁定定義時所在的對象,而不是使用時所在的對象。
  • 不可以當(dāng)作構(gòu)造函數(shù),也就是說,不可以使用new命令,否則會拋出一個錯誤。
  • 不可以使用arguments對象,該對象在函數(shù)體內(nèi)不存在。

關(guān)于this對象,下面的代碼將它綁定定義時的對象。

var handler = {

    id: "123456",

    init: function() {
        document.addEventListener("click",
                event => this.doSomething(event.type), false);
    },

    doSomething: function(type) {
        console.log("Handling " + type  + " for " + this.id);
    }
};

上面代碼的init和doSomething方法中,都使用了箭頭函數(shù),它們中的this都綁定handler對象。否則,doSomething方法內(nèi)部的this對象就指向全局對象,運行時會報錯。

函數(shù)參數(shù)的默認(rèn)值

ECMAScript 6 允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值。

function Point(x = 0, y = 0) {
   this.x = x;
   this.y = y;
}

var p = new Point(); 
// p = { x:0, y:0 }

模板字符串

模板字符串(template string)是增強版的字符串,即可以當(dāng)作普通字符串使用,也可以在字符串中嵌入變量。它用反引號(`)標(biāo)識。

// 普通字符串
`In JavaScript '\n' is a line-feed.`

// 多行字符串
`In JavaScript this is
 not legal.`

// 字符串中嵌入變量
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

var x = 1;
var y = 2;
console.log(`${ x } + ${ y } = ${ x + y}`) 
// "1 + 2 = 3"

for...of循環(huán)

JavaScript原有的for...in循環(huán),只能獲得對象的鍵名,不能直接獲取鍵值。ES6提供for...of循環(huán),允許遍歷獲得鍵值。

var arr = ["a", "b", "c", "d"];
for (a in arr) {
  console.log(a);
}
// 0
// 1
// 2
// 3

for (a of arr) {
  console.log(a); 
}
// a
// b
// c
// d

上面代碼表明,for...in循環(huán)讀取鍵名,for...of循環(huán)讀取鍵值。

for...of循環(huán)還可以遍歷對象。

var es6 = {
  edition: 6,
  committee: "TC39",
  standard: "ECMA-262"
};

for (e in es6) {
  console.log(e);
}
// edition
// committee
// standard

var engines = Set(["Gecko", "Trident", "Webkit", "Webkit"]);
for (var e of engines) {
    console.log(e);
}
// Gecko
// Trident
// Webkit

var es6 = new Map();
es6.set("edition", 6);
es6.set("committee", "TC39");
es6.set("standard", "ECMA-262");
for (var [name, value] of es6) {
  console.log(name + ": " + value);
}
// edition: 6
// committee: TC39
// standard: ECMA-262

上面代碼一共包含三個例子,第一個是for...in循環(huán)的例子,后兩個是for...of循環(huán)的例子。最后一個例子是同時遍歷對象的鍵名和鍵值。

數(shù)組推導(dǎo)

(1)基本用法

ES6提供簡潔寫法,允許直接通過現(xiàn)有數(shù)組生成新數(shù)組,這被稱為數(shù)組推導(dǎo)(array comprehension)。

var a1 = [1, 2, 3, 4];
var a2 = [i * 2 for (i of a1)];

a2 // [2, 4, 6, 8]

上面代碼表示,通過for...of結(jié)構(gòu),數(shù)組a2直接在a1的基礎(chǔ)上生成。

數(shù)組推導(dǎo)可以替代map和filter方法。

[for (i of [1, 2, 3]) i * i];
// 等價于
[1, 2, 3].map(function (i) { return i * i });

[i for (i of [1,4,2,3,-8]) if (i < 3)];
// 等價于
[1,4,2,3,-8].filter(function(i) { return i < 3 });

上面代碼說明,模擬map功能只要單純的for...of循環(huán)就行了,模擬filter功能除了for...of循環(huán),還必須加上if語句。

(2)多重推導(dǎo)

新引入的for...of結(jié)構(gòu),可以直接跟在表達式的前面或后面,甚至可以在一個數(shù)組推導(dǎo)中,使用多個for...of結(jié)構(gòu)。

var a1 = ["x1", "y1"];
var a2 = ["x2", "y2"];
var a3 = ["x3", "y3"];

[(console.log(s + w + r)) for (s of a1) for (w of a2) for (r of a3)];
// x1x2x3
// x1x2y3
// x1y2x3
// x1y2y3
// y1x2x3
// y1x2y3
// y1y2x3
// y1y2y3

上面代碼在一個數(shù)組推導(dǎo)之中,使用了三個for...of結(jié)構(gòu)。

需要注意的是,數(shù)組推導(dǎo)的方括號構(gòu)成了一個單獨的作用域,在這個方括號中聲明的變量類似于使用let語句聲明的變量。

(3)字符串推導(dǎo)

由于字符串可以視為數(shù)組,因此字符串也可以直接用于數(shù)組推導(dǎo)。

[c for (c of 'abcde') if (/[aeiou]/.test(c))].join('') // 'ae'

[c+'0' for (c of 'abcde')].join('') // 'a0b0c0d0e0'

上面代碼使用了數(shù)組推導(dǎo),對字符串進行處理。

上一部分的數(shù)組推導(dǎo)有一個缺點,就是新數(shù)組會立即在內(nèi)存中生成。這時,如果原數(shù)組是一個很大的數(shù)組,將會非常耗費內(nèi)存。

多變量賦值

ES6允許簡潔地對多變量賦值。正常情況下,將數(shù)組元素賦值給多個變量,只能一次次分開賦值。

var a = 1;
var b = 2;
var c = 3;

ES6允許寫成下面這樣。

var [a, b, c] = [1, 2, 3];

本質(zhì)上,這種寫法屬于模式匹配,只要等號兩邊的模式相同,左邊的變量就會被賦予對應(yīng)的值。下面是一些嵌套數(shù)組的例子。

var [foo, [[bar], baz]] = [1, [[2], 3]]

var [,,third] = ["foo", "bar", "baz"]

var [head, ...tail] = [1, 2, 3, 4]

它還可以接受默認(rèn)值。

var [missing = true] = [];
console.log(missing)
// true

var { x = 3 } = {};
console.log(x)
// 3

它不僅可以用于數(shù)組,還可以用于對象。

var { foo, bar } = { foo: "lorem", bar: "ipsum" };

foo // "lorem"
bar // "ipsum"

var o = {
  p1: [
    "Hello",
    { p2: "World" }
  ]
};

var { a: [p1, { p2 }] } = o;

console.log(p1)
// "Hello"

console.log(p2)
// "World"

這種寫法的用途很多。

(1)交換變量的值。

[x, y] = [y, x];

(2)從函數(shù)返回多個值。

function example() {
    return [1, 2, 3];
}

var [a, b, c] = example();

(3)函數(shù)參數(shù)的定義。

function f({p1, p2, p3}) {
  // ...
}

(4)函數(shù)參數(shù)的默認(rèn)值。

jQuery.ajax = function (url, {
  async = true,
  beforeSend = function () {},
  cache = true,
  complete = function () {},
  crossDomain = false,
  global = true,
  // ... more config
}) {
  // ... do stuff
};

數(shù)據(jù)結(jié)構(gòu)

class結(jié)構(gòu)

(1)基本用法

ES6提供了“類”(class)。此前,一般用構(gòu)造函數(shù)模擬“類”。

// ES5
var Language = function(config) {
  this.name = config.name;
  this.founder = config.founder;
  this.year = config.year;
};

Language.prototype.summary = function() {
  return this.name+"由"+this.founder+"在"+this.year+"創(chuàng)造";
};

// ES6
class Language {
  constructor(name, founder, year) {
    this.name = name;
    this.founder = founder;
    this.year = year;
  }

  summary() {
    return this.name+"由"+this.founder+"在"+this.year+"創(chuàng)造";
  }
}

在上面代碼中,ES6用constructor方法,代替ES5的構(gòu)造函數(shù)。

(2)繼承

ES6的class結(jié)構(gòu)還允許使用extends關(guān)鍵字,表示繼承。

class MetaLanguage extends Language {
  constructor(x, y, z, version) {
    super(x, y, z);
    this.version = version;
  }
  summary() {
    //...
    super.summary();
  }
}

上面代碼的super方法,表示調(diào)用父類的構(gòu)造函數(shù)。

module定義

(1)基本用法

ES6允許定義模塊。也就是說,允許一個JavaScript腳本文件調(diào)用另一個腳本文件。

假設(shè)有一個circle.js,它是一個單獨模塊。

// circle.js

export function area(radius) {
  return Math.PI * radius * radius;
}

export function circumference(radius) {
  return 2 * Math.PI * radius;
}

然后,main.js引用這個模塊。

// main.js

import { area, circumference } from 'circle';

console.log("圓面積:" + area(4));
console.log("圓周長:" + circumference(14));

另一種寫法是整體加載circle.js。

// main.js

module circle from 'circle';

console.log("圓面積:" + circle.area(4));
console.log("圓周長:" + circle.circumference(14));

(2)模塊的繼承

一個模塊也可以繼承另一個模塊。

// circleplus.js

export * from 'circle';
export var e = 2.71828182846;
export default function(x) {
    return Math.exp(x);
}

加載上面的模塊。

// main.js

module math from "circleplus";
import exp from "circleplus";
console.log(exp(math.pi);

(3)模塊的默認(rèn)方法

還可以為模塊定義默認(rèn)方法。

// circleplus.js

export default function(x) {
    return Math.exp(x);
}

ECMAScript 7

2013年3月,ECMAScript 6的草案封閉,不再接受新功能了。新的功能將被加入ECMAScript 7。根據(jù)JavaScript創(chuàng)造者Brendan Eich的設(shè)想,ECMAScript 7將使得JavaScript更適于開發(fā)復(fù)雜的應(yīng)用程序和函數(shù)庫。

ECMAScript 7可能包括的功能有:

  • Object.observe:對象與網(wǎng)頁元素的雙向綁定,只要其中之一發(fā)生變化,就會自動反映在另一者上。

  • Multi-Threading:多線程支持。目前,Intel和Mozilla有一個共同的研究項目RiverTrail,致力于讓JavaScript多線程運行。預(yù)計這個項目的研究成果會被納入ECMAScript標(biāo)準(zhǔn)。

  • Traits:它將是“類”功能(class)的一個替代。通過它,不同的對象可以分享同樣的特性。

其他可能包括的功能還有:更精確的數(shù)值計算、改善的內(nèi)存回收、增強的跨站點安全、類型化的更貼近硬件的(Typed, Low-level)操作、國際化支持(Internationalization Support)、更多的數(shù)據(jù)結(jié)構(gòu)等等。

參考鏈接

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號