1. 程式人生 > >ES6常用語法(下)

ES6常用語法(下)

  • Symbol型別
     ES5 的物件屬性名都是字串,這容易造成屬性名的衝突。比如,你使用了一個他人提供的物件,但又想為這個物件新增新的方法,新方法的名字就有可能與現有方法產生衝突。如果有一種機制,保證每個屬性的名字都是獨一無二的就好了,這樣就從根本上防止屬性名的衝突。這就是 ES6 引入 Symbol的原因        ES6 引入了一種新的原始資料型別 Symbol,表示獨一無二的值。它是 JavaScript 語言的第七種資料型別,前六種是: undefined
null、布林值(Boolean)、字串(String)、數值(Number)、物件(Object)        Symbol 值通過 Symbol函式生成。這就是說,物件的屬性名現在可以有兩種型別,一種是原來就有的字串,另一種就是新增的 Symbol 型別。凡是屬性名屬於 Symbol 型別,就都是獨一無二的,可以保證不會與其他屬性名產生衝突   let s = Symbol();

typeof s
// "symbol" 上面程式碼中,變數 s就是一個獨一無二的值。 typeof
運算子的結果,表明變數 s是 Symbol 資料型別,而不是字串之類的其他型別 注意, Symbol函式前不能使用 new命令,否則會報錯。這是因為生成的 Symbol 是一個原始型別的值,不是物件。也就是說,由於 Symbol 值不是物件,所以不能新增屬性。基本上,它是一種類似於字串的資料型別。 Symbol函式可以接受一個字串作為引數,表示對 Symbol 例項的描述,主要是為了在控制檯顯示,或者轉為字串時,比較容易區分。   let s1 = Symbol('foo');let s2 = Symbol('bar');

s1 // Symbol(foo)
s2 // Symbol(bar)

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)" 上面程式碼中, s1
s2是兩個 Symbol 值。如果不加引數,它們在控制檯的輸出都是 Symbol(),不利於區分。有了引數以後,就等於為它們加上了描述,輸出的時候就能夠分清,到底是哪一個值。   注意, Symbol函式的引數只是表示對當前 Symbol 值的描述,因此相同引數的 Symbol函式的返回值是不相等的。     // 沒有引數的情況 let s1 = Symbol(); let s2 = Symbol();
s1 === s2 // false

// 有引數的情況 let s1 = Symbol('foo'); let s2 = Symbol('foo');
s1 === s2 // false 上面程式碼中, s1s2都是 Symbol函式的返回值,而且引數相同,但是它們是不相等的。 Symbol 值也可以轉為布林值,但是不能轉為數值     let sym = Symbol(); Boolean(sym) // true !sym // false
if (sym) {
// ...
}

Number(sym) // TypeError
sym + 2 // TypeError   作為屬性名的 Symbol 由於每一個 Symbol 值都是不相等的,這意味著 Symbol 值可以作為識別符號,用於物件的屬性名,就能保證不會出現同名的屬性,能防止某一個鍵被不小心改寫或覆蓋。   // 第一種寫法
let a = {};
a[mySymbol] = 'Hello!';
// 第二種寫法
let a = {   [mySymbol]: 'Hello!'}; // 以上寫法都得到同樣結果
a[mySymbol] // "Hello!" 注意,Symbol 值作為物件屬性名時,不能用點運算子。   const mySymbol = Symbol();
const a = {};

a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'] // "Hello!" 上面程式碼中,因為點運算子後面總是字串,所以不會讀取 mySymbol作為標識名所指代的那個值,導致 a的屬性名實際上是一個字串,而不是一個 Symbol 值。 同理,在物件的內部,使用 Symbol 值定義屬性時,Symbol 值必須放在方括號之中。     let s = Symbol();

let obj = {
  [s]: function (arg) { ... }};

obj[s](123); 上面程式碼中,如果 s不放在方括號中,該屬性的鍵名就是字串 s,而不是 s所代表的那個 Symbol 值。    
   
  • Set和Map結構
ES6 提供了新的資料結構 Set。它類似於陣列,但是成員的值都是唯一的,沒有重複的值。 Set 本身是一個建構函式,用來生成 Set 資料結構。   const s = new Set();

[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));

for (let i of s) {
  console.log(i);}
// 2 3 5 4 //s=[2,3,5,4]  // 陣列去重的方法(1)   上面程式碼 通過add方法向 Set 結構加入成員,結果表明 Set 結構不會新增重複的值。   Set 函式可以接受一個數組(獲取dom的nodelist物件)作為引數,用來初始化。   // 例一
const set = new Set([1, 2, 3, 4, 4]);           [...set]
// [1, 2, 3, 4]

// 例二
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5

// 例三 const set = new Set(document.querySelectorAll('div')); set.size // 56 上面程式碼也展示了一種去除陣列重複成員的方法。   // 去除陣列的重複成員(2)
[...new Set(array)] 向 Set 加入值的時候,不會發生型別轉換,所以 5"5"是兩個不同的值。Set 內部判斷兩個值是否不同,使用的演算法叫做“Same-value-zero equality”,它類似於精確相等運算子( ===),主要的區別是 NaN等於自身,而精確相等運算子認為 NaN不等於自身。   let set = new Set(); let a = NaN; let b = NaN; set.add(a); set.add(b); set // Set {NaN}   Set 結構的例項有以下屬性
  • constructor:建構函式,預設就是Set函式。
  • size:返回Set例項的成員總數。
Set 例項的方法分為兩大類:操作方法(用於操作資料)和遍歷方法(用於遍歷成員)。下面四個操作方法   - add(value):新增某個值,返回 Set 結構本身。 - 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   Array.from方法可以將 Set 結構轉為陣列。     const items = new Set([1, 2, 3, 4, 5]); const array = Array.from(items);   Set 結構的例項預設可遍歷   let set = new Set(['red', 'green', 'blue']);

for (let x of set) {
  console.log(x);}
// red
// green
// blue   Set 結構的例項與陣列一樣,也擁有 forEach方法,用於對每個成員執行某種操作,沒有返回值。     set = new Set([1, 4, 9]); set.forEach((value, key) => console.log(key + ' : ' + value)) // 1 : 1
// 4 : 4
// 9 : 9 上面程式碼說明, forEach方法的引數就是一個處理函式。該函式的引數與陣列的 forEach一致,依次為鍵值、鍵名、集合本身(上例省略了該引數)。這裡需要注意,Set 結構的鍵名就是鍵值(兩者是同一個值),因此第一個引數與第二個引數的值永遠都是一樣的。   擴充套件運算子和 Set 結構相結合,就可以去除陣列的重複成員。   let arr = [3, 5, 2, 2, 5, 5]; let unique = [...new Set(arr)]; // [3, 5, 2]   map結構 JavaScript 的物件(Object),本質上是鍵值對的集合(Hash 結構),但是傳統上只能用字串當作鍵。這給它的使用帶來了很大的限制。   ES6 提供了 Map 資料結構。它類似於物件,也是鍵值對的集合,但是“鍵”的範圍不限於字串,各種型別的值(包括物件)都可以當作鍵。也就是說,Object 結構提供了“字串—值”的對應,Map 結構提供了“值—值”的對應,是一種更完善的 Hash 結構實現。如果你需要“鍵值對”的資料結構,Map 比 Object 更合適。    

Map 結構的例項有以下屬性和操作方法。

(1)size 屬性

  size屬性返回 Map 結構的成員總數。   const map = new Map();
map.set('foo', true);
map.set('bar', false);

map.size // 2  

(2)set(key, value)

  set方法設定鍵名 key對應的鍵值為 value,然後返回整個 Map 結構。如果 key已經有值,則鍵值會被更新,否則就新生成該鍵。   const m = new Map();

m.set('edition', 6)      // 鍵是字串
m.set(262, 'standard')    // 鍵是數值
m.set(undefined, 'nah')  // 鍵是 undefined

    set方法返回的是當前的 Map物件,因此可以採用鏈式寫法。   let map = new Map()
  .set(1, 'a')
  .set(2, 'b')
  .set(3, 'c');     (3)get(key)   get方法讀取 key對應的鍵值,如果找不到 key,返回 undefined。   const m = new Map();

const hello = function() {console.log('hello');};
m.set(hello, 'Hello ES6!') // 鍵是函式

m.get(hello) // Hello ES6!

    (4)has(key)   has方法返回一個布林值,表示某個鍵是否在當前 Map 物件之中。     const m = new Map();

m.set('edition', 6);
m.set(262, 'standard');
m.set(undefined, 'nah');

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

      (5)delete(key)   delete方法刪除某個鍵,返回 true。如果刪除失敗,返回 false  const m = new Map();
m.set(undefined, 'nah');
m.has(undefined)    // true

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

  (6)clear()   clear方法清除所有成員,沒有返回值。   let map = new Map(); map.set('foo', true); map.set('bar', false); map.size // 2 map.clear() map.size // 0  

Map 結構原生提供三個遍歷器生成函式和一個遍歷方法。

  • keys():返回鍵名的遍歷器。
  • values():返回鍵值的遍歷器。
  • entries():返回所有成員的遍歷器。
  • forEach():遍歷 Map 的所有成員。
  需要特別注意的是,Map 的遍歷順序就是插入順序     const map = new Map([
  ['F', 'no'],
  ['T',  'yes'],]);

for (let key of map.keys()) {
  console.log(key);}
// "F"
// "T"
for (let value of map.values()) {
  console.log(value);}
// "no"
// "yes"
for (let item of map.entries()) {
  console.log(item[0], item[1]);}
// "F" "no"
// "T" "yes"

// 或者
for (let [key, value] of map.entries()) {
  console.log(key, value);}
// "F" "no"
// "T" "yes"

// 等同於使用map.entries()
for (let [key, value] of map) {
  console.log(key, value);}
// "F" "no"
// "T" "yes" Map 結構轉為陣列結構,比較快速的方法是使用擴充套件運算子( ...)。   const map = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],]);

[...map.keys()]
// [1, 2, 3]
[...map.values()]
// ['one', 'two', 'three']
[...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']]
[...map]
// [[1,'one'], [2, 'two'], [3, 'three']]  
   
  • Generators生成器函式
     Generator 函式是 ES6 提供的一種非同步程式設計解決方案,語法行為與傳統函式完全不同      Generator 函式有多種理解角度。語法上,首先可以把它理解成,Generator 函式是一個狀態機,封裝了多個內部狀態。      執行 Generator 函式會返回一個遍歷器物件,也就是說,Generator 函式除了狀態機,還是一個遍歷器物件生成函式。返回的遍歷器物件,可以依次遍歷 Generator 函式內部的每一個狀態。      形式上,Generator 函式是一個普通函式,但是有兩個特徵。一是, function關鍵字與函式名之間有一個星號;二是,函式體內部使用 yield表示式,定義不同的內部狀態( yield在英語裡的意思就是“產出”)   function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';}

var hw = helloWorldGenerator(); 上面程式碼定義了一個 Generator 函式 helloWorldGenerator,它內部有兩個 yield表示式( helloworld),即該函式有三個狀態:hello,world 和 return 語句(結束執行)。        然後,Generator 函式的呼叫方法與普通函式一樣,也是在函式名後面加上一對圓括號。不同的是,呼叫 Generator 函式後,該函式並不執行,返回的也不是函式執行結果,而是一個指向內部狀態的指標物件   yield 表示式   由於 Generator 函式返回的遍歷器物件,只有呼叫 next方法才會遍歷下一個內部狀態,所以其實提供了一種可以暫停執行的函式。 yield表示式就是暫停標誌。

(1)遇到yield表示式,就暫停執行後面的操作,並將緊跟在yield後面的那個表示式的值,作為返回的物件的value屬性值。

(2)下一次呼叫next方法時,再繼續往下執行,直到遇到下一個yield表示式。

(3)如果沒有再遇到新的yield表示式,就一直執行到函式結束,直到return語句為止,並將return語句後面的表示式的值,作為返回的物件的value屬性值。

  (4)如果該函式沒有 return語句,則返回的物件的 value屬性值為 undefined        下一步,必須呼叫遍歷器物件的 next方法,使得指標移向下一個狀態。也就是說,每次呼叫 next方法,內部指標就從函式頭部或上一次停下來的地方開始執行,直到遇到下一個 yield表示式(或 return語句)為止。換言之,Generator 函式是分段執行的, yield表示式是暫停執行的標記,而 next方法可以恢復執行。   hw.next()
// { value: 'hello', done: false }

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

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

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

上面程式碼一共呼叫了四次next方法。

第一次呼叫,Generator 函式開始執行,直到遇到第一個yield表示式為止。next方法返回一個物件,它的value屬性就是當前yield表示式的值hellodone屬性的值false,表示遍歷還沒有結束。

第二次呼叫,Generator 函式從上次yield表示式停下的地方,一直執行到下一個yield表示式。next方法返回的物件的value屬性就是當前yield表示式的值worlddone屬性的值false,表示遍歷還沒有結束。

第三次呼叫,Generator 函式從上次yield表示式停下的地方,一直執行到return語句(如果沒有return語句,就執行到函式結束)。next方法返回的物件的value屬性,就是緊跟在return語句後面的表示式的值(如果沒有return語句,則value屬性的值為undefined),done屬性的值true,表示遍歷已經結束。

  第四次呼叫,此時 Generator 函式已經執行完畢, next方法返回物件的 value屬性為 undefineddone屬性為 true。以後再呼叫 next方法,返回的都是這個值。   總結一下,呼叫 Generator 函式,返回一個遍歷器物件,代表 Generator 函式的內部指標。以後,每次呼叫遍歷器物件的 next方法,就會返回一個有著 valuedone兩個屬性的物件。 value屬性表示當前的內部狀態的值,是 yield表示式後面那個表示式的值; done屬性是一個布林值,表示是否遍歷結束。