ECMAScript 6入門


預計在2014年底,ECMAScript 6將會正式發布,他的草案在13年3月份被凍結,后續提出新特性將會移至ECMASript 7中。目前還沒有哪款瀏覽器實現了ES6的全部內容,兼容性最強的一款要數FireFox了。具體情況可以在這里查看。

關於 ECMAScript 6 草案,我在博客里頭復制了一份,可以點擊這里

JavaScript的內容是越來越豐富,在ES6中還添加了模塊(module)和類(class),感覺他已經失去了曾經的單純了,不知道這些新功能的補充對開發者來說是福音還是負擔。之前寫過兩篇關於ES6的文章,ECMAScript 6 簡介ECMAScript 6中的let和const關鍵詞,本文將一一介紹ES6中的一些新特性。

本文地址:http://www.cnblogs.com/hustskyking/p/ecmascript6-overview.html,轉載請注明源地址。

注意:如果想測試以下屬性,請安裝 0.11+ 版本的 node,並添加上 --harmony 參數。

一、let 和 const

這個內容在 ECMAScript 6中的let和const關鍵詞 一文中已經介紹過了。簡單來說就是一句話:ES6中引入了塊級作用域,let的有效區間是他所在的 {} 大括號中。const 為常量,定義之后不能更改,也刪除不了。

> const PI = 3.14;
> Object.getOwnPropertyDescriptor(window, PI) Object {value: 3.1415, writable: false, enumerable: true, configurable: false}

writable 和 configurable 都是 false。

二、多變量的模式賦值

寫過 coffee-script 的人都知道,我們可以這樣給一個數據賦值:

num[1..3] = ["hello", "i'am", "Barret Lee"]

ES6中也允許類似的多變量賦值:

var [x, y, z] = ["hello", "i'am", "Barret Lee"];

更強大的是,他還適合對象:

var { foo, bar } = { foo: "Barret", bar: "Lee" };

這種賦值方式是模式匹配的,只要左側跟右側對應,便可以成功賦值。感覺新手不會太適應這種寫法。

三、數組推導

先看例子:

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

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

這東西只是簡化了編程,沒有從根本上增加功能和特性,寫 coffee 的人應該比較喜歡,我看着還是有點不習慣,其實上面的寫法就等價於:

var a1 = [1, 2, 3, 4];
var a2 = a1.map(function (i) { return i * 2 });

我想除非是代碼長度有限制,否則這玩意兒正式出來了我也不會用它。

四、字符串的擴展

這一塊的內容相當於是給 JS 編碼打一個補丁,這個補丁用來彌補雙字節 UTF-16 字符帶來的問題,引入的各個函數也只是對不同場景的修復。這個擴展還是相當重要的,尤其是 ArrayBuffer 中數據類型的相關處理,涉及到很多類似 Float64Array Uint32Array 等類型化數組的處理,我在 你所不知道的JavaScript數組 曾提到過。

1. codePointAt

這個地方需要解釋下 JavaScript 對字符的儲存模式,JavaScript 中的字符串是以 UTF-16 為代碼單元,通常我們使用的字符范圍都在 Unicode 值 0x10000 以內,他們對應的 UTF-16 就是它們自身,但 Unicode 中也存在這個范圍之外的字符,這時候就需要兩個 UTF-16 字符來描述,比如:

alert("𠐀".length); //2

因為字符串的 length 表示的並不是字符個數,而是 UTF-16 的單元個數。關於這方面知識的具體介紹,可以戳這里

ES6提供了 codePointAt 函數來正確處理 4 個字節儲存的字符。

var s = "𠐀二";
s.codePointAt(0);

codePointAt 會把 s 中的兩個字符都正確解析出來,相當於智能的將儲存單元切換為 2 或者 4.

2. fromCodePoint

對應 String.fromCharCode 的能夠智能解析 s 的函數是 String.fromCodePoint

3. 字符的Unicode表示法

我們知道可以使用 \u0000-\uFFFF 來表示一個 Unicode 字符,依然是上面的問題,如果字符超出了這個范圍,比如 "\u10000" 這個字符,便不能正確的解析出來,ES6中可以這樣:

#\u{10000}

用大括號括起來就可以正常解析超過 FFFF 的字符了。

4. 正則修飾符 u

/^.$/.test("𠐀") // false
/^..$/.test("𠐀") // true
/^.$/u.test("𠐀") // true

看到上面三個測試,相信你已經知道是什么意思了。

5. 幾個沒啥意思的函數

contains(), startsWith(), endsWith(), repeat()

都是對 String 的拓展,顧名就能思意。

6. 模板字符串

之前寫過一篇關於 TEMPLATE 標簽的文章,這里我們又看到了一個跟模板相關的東西。

// 字符串中嵌入變量
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"

配合 TEMPLATE 標簽使用是比較方便的,不過 TEMPLATE 標簽可以很方便的使用自定義標簽,這個特性也不是很突出了。

<div id="test">
  <x-from>DOM文檔</x-from>
  <x-name>test元素</x-name>
</div>

<template id="temp">
  我是來自 <strong><content select="x-from"></content></strong>
  中的 <strong><content select="x-name"></content></strong>
  的數據。
</template>

<script>
var root = test.webkitCreateShadowRoot();
root.appendChild(document.importNode(temp.content));
</script>

五、數值的擴展

1. 二進制和八進制表示法

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

前綴 0b 和 0o 表示二進制和八進制數值

2. 擴展函數

  • Number.isFinite()
  • Number.isNaN()
  • Number.parseInt()
  • Number.parseFloat()
  • Number.isInteger()

具體細節可以去 MDN 上搜查。

六、對象的擴展

在 ES5.1 中我們看到了 Object 的靈活性提高了很多,加入了 create、defineProperty、freeze 等等很多十分有用的方法,而在 ES6 中,依然繼續加強:

1. Object.is()

比較嚴格相等,傳入兩個參數,其功能跟 === 差不多,不同的是:+0不等於-0,NaN與本身恆等

+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

2. proto屬性

obj.__proto__ = otherObj;

這東西瀏覽器早就實現了,只是在 ES6 才被納入標准中,還記得這東西被用來判斷是否為 IE 么:

var isIE = "__proto__" in {} ? false : true;

3. 增強的對象寫法

又是一個裝飾品:

var Person = {
  name: 'Barret Lee',

  //等同於birth: birth
  birth,

  // 等同於hello: function ()...
  hello() { console.log('My Name Is', this.name); }

}; 

4. 允許變量滲入 key 中

var name = "my name";
var Me = {
    [name]: "Barret Lee",
    "name": "李靖"
};
Me[name]       // "Barret Lee"
Me["my name"]  // "Barret Lee"
Me["name"]     // "李靖"

挺方便的,不過容易出錯,比如上面,看暈了吧~

5. Symbol

一種從類型上區分數據的工具,他是一個原始類型的值,不是對象,很適合做標識符。

6. Proxy

Proxy 內置的一個代理工具,使用他可以在對象處理上加一層屏障:

var plain = {
    name : "Barret Lee"
};
var proxy = new Proxy(plain, {
    get: function(target, property) {
        return property in target ? target[property] : "我擦";
    }
});

proxy.name // "Barret Lee"
proxy.title // "我擦"

Proxy(target, handler), 這里的 handler 可以是 set get has hasOwn keys 等等方法,具體可以移步這里:http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies

七、函數的擴展

1. 參數設置默認值

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

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

他這個參數可以初始化相當於暴露更多的底層接口吧,提高了函數的拓展性。

2. rest運算符(...)

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

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

   return sum;
}
add(1, 2 ,3)  // 6

這里的 values 需要用 val of values 來遍歷。

3. 箭頭函數(=>)

之前寫過 coffee ,所以對這個我還是比較熟悉的:

var f = (a, b) => {return a + b};

這個跟上面提到的一些內容一樣,都是裝飾性的,沒太多用途。

八、Set和Map數據結構

1. Set

簡單點解釋,他就是一個沒有重復數值的數組。或者說他就是一個數組的 hash 表。

var items = new Set([1,2,3,4,5,5,5,5]);

for (i of s) {console.log(i)}
// 2 3 4 5

其遍歷也是使用 val of values,他沒有繼承 Array 的方法,自己的幾個方法是:

  • size():返回成員總數。
  • add(value):添加某個值。
  • delete(value):刪除某個值。
  • has(value):返回一個布爾值,表示該值是否為set的成員。
  • clear():清除所有成員。

轉化為數組的方式:

var items = new Set([1, 2, 3, 4, 5]);
var array = Array.from(items);

2. Map

Map 是一個“超對象”,其 key 除了可以是 String 類型之外,還可以為其他類型(如:對象)

var m = new Map();

o = {p: "Hello World"};

m.set(o, "content")

console.log(m.get(o))
// "content"

他的方法和 Set 差不多:

  • size:返回成員總數。
  • set(key, value):設置一個鍵值對。
  • get(key):讀取一個鍵。
  • has(key):返回一個布爾值,表示某個鍵是否在Map數據結構中。
  • delete(key):刪除某個鍵。
  • clear():清除所有成員。
  • keys():返回鍵名的遍歷器。
  • values():返回鍵值的遍歷器。
  • entries():返回所有成員的遍歷器。

3. WeakMap

如果說Map 是一個“超對象”,那 WeakMap 就是個 “弱超對象”,他的鍵值只能是除 null 以外的對象。他的存在是為了方便垃圾回收。

八、遍歷器(Iterator)

相比 Array,他對數據的管理更為緊湊,而且可操縱性也比較強,以后會成為一個比較通用的 JS 工具。

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'

一個對象只要具備了next方法,就可以用for...of循環遍歷它的值。

for (var n of it) {
  if (n > 5)
    break;
  console.log(n);
}

九、Generator 函數

Generator就是一個改裝了的 Iterator 遍歷器,通過 yield 來增加一個 next() 節點。

聲明一個 Generator 的方法:

function * foo( input ) {
    var res = yield input;
}

使用方式:

function * foo( input ) {
  var res = yield input;
}

var g = foo(10);
g.next(); // { value: 10, done: false }
g.next(); // { value: undefined, done: true }

Genrator的標識就是函數名前面有個 * 號,由於每個 yield 都會增加一個 next() 節點,當我們在一個 Generator 中添加多個 yield 的時候:

function* G() {
    yield 'Barret';
    yield 'Lee';
}

var g = G();
g.next(); // "Barret"
g.next(); // "Lee"
g.next(); // undefined
g.next(); // Error: Generator has already finished

相比 Iterator,我更喜歡 Generator。

十、Promise 對象

ES6將 Promise 納入規范之后,很多瀏覽器根據 Promise/A+ 規范實現了一套 API,Promise對象是對異步操作的平坦式表達,避免了 callback hell,也就是多層嵌套的回調函數。這方面的東西可以參考我之前寫的 JavaScript異步編程原理.

這里是 Promise/A+ 規范文檔,感興趣的可以閱讀一下。

在Promises/A規范中,每個任務都有三種狀態:默認(pending)、完成(fulfilled)、失敗(rejected)。

  • 默認狀態可以單向轉移到完成狀態,這個過程叫resolve,對應的方法是deferred.resolve(promiseOrValue);
  • 默認狀態還可以單向轉移到失敗狀態,這個過程叫reject,對應的方法是deferred.reject(reason);
  • 默認狀態時,還可以通過deferred.notify(update)來宣告任務執行信息,如執行進度;
  • 狀態的轉移是一次性的,一旦任務由初始的pending轉為其他狀態,就會進入到下一個任務的執行過程中。

十一、Class和Module

1. Class

Class,對象模板:

class Point {

  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '('+this.x+', '+this.y+')';
  }

}

一下子就有了 C++/Java 的感覺了,在類中可以使用 extends 繼承:

class ColorPoint extends Point {

  constructor(x, y, color) {
    super(x, y); // same as super.constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color+' '+super();
  }

}

2. Module

之前在這篇文章談過模塊化編程的必要性,時代在變化,需求也在變,ES6引入 Module 也算是與時俱進。ES6允許將獨立的js文件作為模塊,也就是說,允許一個JavaScript腳本文件調用另一個腳本文件,從而使得模塊化編程成為可能。

// circle.js

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

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

如果我們要引入 circle.js 的函數,可以:

// main.js

import { area, circumference } from 'circle';

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

如果要引入整個 circle 的內容:

// main.js

module circle from 'circle';

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

模塊之間的繼承使用這條命令:

// other.js
export * from 'circle';

這個東西,很好用,可惜了,純潔的 JS,被糟蹋成啥樣了=. =

十二、感謝

本文的書寫邏輯參考阮一峰的總結,並參入了一些個人主觀色彩,感謝前人栽樹!

十三、參考資料

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM