ES6入門之Symbol

1. 概述

由於ES5的對象屬性名都是字符串,這容易形成屬性名的衝突。好比,你使用了他人提供的對象,但又想爲這個對象添加新的方法,新方法的名字就有可能與現有的方法產生衝突,全部就有了Symbol,它會保證每一個屬性名是獨一無二的,能夠從根本上防止屬性名的衝突。前端

ES6引入了一種新的原始數據類型 Symbol,表示獨一無二的值,它是Javascript語言的第七種數據類型。segmentfault

Symbol值經過 Symbol函數生成,對象的屬性名如今能夠有兩種類型,一種是原有的字符串,另外一種就是新增的Symbol類型。 Symbol類型是獨一無二的不會和其餘屬性名產生衝突數組

let s = Symbol()
typeof s
// 'Symbol'
複製代碼

注意: Symbol 函數前不能用 new命令,不然會報錯。由於生成的Symbol是一個原始類型的值,不是對象。**Symbol值不是對象,沒法爲其添加屬性。**它是一個相似字符串的數據類型函數

Symbol函數能夠接受一個參數,表示對Symbol實例的描述,方便在控制檯顯示,在轉爲字符串的時候方便區分,以下:post

let s1 = Symbol('foo');
let s2 = Symbol('far');

s1 // Symbol(foo)
s2 // Symbol(far)

s1.toString()   //  'Symbol(foo)'
s2.toString()   //  'Symbol(far)'

// 經過添加描述能夠區分究竟是哪一個Symbol值輸出了
複製代碼

若是 Symbol的參數是一個對象,那麼會調用該對象的toString()方法將其轉換成字符串,在生成Symbol值spa

const obj = {
    toString() {
        retrun 'abc'
    }
}
const sym = Symbol(obj)
sym // Symbol(abc)

// 注意: Symbol函數的參數只是表示對當前Symbol值的描述,
所以相同參數的Symbol函數的返回值不一樣

let s1 = Symbol()
let s2 = Symbol()

s1 === s2 // false

let s1 = Symbol('cc')
let s2 = Symbol('cc')

s1 === s2 // false
複製代碼

注意: Symbol 值不能與其餘類型的值進行運算,會報錯。可是,Symbol值能夠顯式轉爲字符串。另外,Symbol值能夠轉換爲布爾值,可是不能轉爲數值。prototype

2. Symbol.prototype.description

建立 Symbol的時候能夠添加一個描述。若是讀取這個描述,則須要將Symbol顯式轉爲字符串。可是這樣很不方便,因此在ES2019中提供 實例屬性 description 直接返回 Symbol的描述。3d

// 添加描述
const sym = Symbol('boo')

// 讀取描述
String(sym) // 'Symbol(boo)'
sym.toString()  // 'Symbol(boo)'

// 新增方法
sym.description // 'boo'
複製代碼

3. 做爲屬性名的 Symbol

由於 每個 Symbol值都是不相等的,這意味着 Symbol 值 能夠做爲標識符,用於對象的屬性名,就能保證不會出現同名的屬性,防止某一個鍵被不當心改寫 或 覆蓋。以下:code

let mySymbol = Symbol()

// 第一種寫法
let a = {}
a[mySymbol] = 'hello'

// 第二種寫法
let a = {
    [mySymbol]: 'hello'
}

// 第三種寫法
let a = {}
Object.defineProperty(a, mySymbol, { value: 'hello' })

// 結果
a[mySymbol] // hello
複製代碼

注意: Symbol值做爲對象屬性名時,不能用點運算符,並且使用Symbol值定義屬性時,Symbol值必須發放在方括號之中。另外,Symbol值做爲屬性名時,該屬性仍是公開屬性,不是私有屬性。cdn

4. 實例:消除魔術字符串

魔術字符串是指,在代碼之中出現屢次、與代碼造成強耦合的某一個具體的字符串或數值。

function getArea(shape, options) {
  let area = 0;

  switch (shape) {
    case 'Triangle': // 魔術字符串
      area = .5 * options.width * options.height;
      break;
    /* ... more code ... */
  }

  return area;
}

getArea('Triangle', { width: 100, height: 100 }); // 魔術字符串
複製代碼

上述代碼中,字符串Triangle就是一個魔術字符串,它屢次出現。與代碼造成「強耦合」,不利於修改維護,經常使用的方法就是把它寫成一個變量。

const shapeType = {
  triangle: 'Triangle'
};

function getArea(shape, options) {
  let area = 0;
  switch (shape) {
    case shapeType.triangle:
      area = .5 * options.width * options.height;
      break;
  }
  return area;
}

getArea(shapeType.triangle, { width: 100, height: 100 });
複製代碼

能夠發現 shapeType.triangle等於哪一個值並不重要,只要確保不會跟其餘的shapeType屬性衝突就行,這樣能夠用 Symbol 來代替。

const shapeType = {
  triangle: Symbol()
};
複製代碼

5. 屬性名遍歷

Symbol做爲屬性名,該屬性不會出如今 for..in | for...of 循環中,也不會被 Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。可是它還有個私有屬性方法能夠獲取指定對象的全部Symbol屬性名。Object.getOwnPropertySymbols,它返回的是一個數組,成員是該對象全部用做屬性名的 Symbol值

const obj = {};
let a = Symbol('a');
let b = Symbol('b');

obj[a] = 'Hello';
obj[b] = 'World';

const objectSymbols = Object.getOwnPropertySymbols(obj);

objectSymbols
// [Symbol(a), Symbol(b)]
複製代碼

咱們還能夠 經過 新API Reflect.ownkeys方法 返回全部類型的鍵名,包括常規鍵名和 Symbol鍵名。

let obj = {
  [Symbol('my_key')]: 1,
  enum: 2,
  nonEnum: 3
};

Reflect.ownKeys(obj)
複製代碼

重點:因爲以Symbol值做爲名稱的屬性,不會被常規的方法遍歷到,咱們能夠利用這個特性爲對象定義一些非私有的,但又但願只用於內部的方法。

6. Symbol.for(),Symbol.keyFor()

有時候咱們但願從新使用同一個 Symbol值,Symbol.for方法能夠作到這一點,它接受一個字符串做爲參數,而後搜索有沒有以該參數做爲名稱的Symbol值。若是又,就返回這個Symbol值,不然就新建並返回一個以該字符串爲名稱的Symbol值

let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');

s1 === s2 // true
複製代碼

區別: Symbol.for() 與 Symbol 這兩種寫法,都會生成新的Symbol,不一樣的是:

  • 前者會被登記在全局環境中供搜索,後者不會。
  • Symbol.for()不會每次調用就返回一個新的Symbol類型的值,而是會先檢查給定的key是否已經存在,若是不存在纔會新建一個值。
  • 調用 30此 Symbol.for('dog'),每次都只返回同一個Symbol,而調用Symbol('dog')則會返回30個不一樣的Symbol值。

Symbol.keyFor方法返回一個已登記的 Symbol 類型值的key 就是和 Symbol.for()對應使用。

let s1 = Symbol.for('foo')
Symbol.keyFor(s1)   // 'foo'

let s2 = Symbol('foo')
Symbol.keyFor(s2)   // undefined
複製代碼

7. 模塊的 Singleton 模式

Singleton 模式指的是調用一個類,任什麼時候候返回的都是同一個實例。能夠經過將實例放到頂層對象global實現。

8. 內置的 Symbol 值

8.1. Symbol.hasInstance

一個內部方法,當其餘對象使用 instanceof 運算符,判斷是否爲該對象的實例時會調用這個方法。

8.2. Symbol.isConcatSpreadable

是一個布爾值,表示該對象用於 Array.prototype.caocat()時,是否能夠展開

  • 數組的默認行爲時能夠展開。
  • 類數組的對象默認不展開。

8.3. Symbol.species

指向一個構造函數,建立衍生對象時,會使用該屬性。它的做用在於,實例對象在運行過程當中,須要再次調用自身的構造函數時,會調用該屬性指定的構造函數。它的主要用途是,有些類庫是在基類的基礎上修改的。那麼子類使用繼承的方法時,但願返回的時基類的實例,而不是子類的實例。

8.4. Symbol.match

指向一個函數,當執行 str.match(myObject)時,若是該屬性存在,則調用它,返回該方法的返回值。

8.5. Symbol.replace

對象的Symbol.replace屬性,指向一個方法。當對象被 String.prototype.replace方法調用的時候,會返回該方法的值。

8.6. Symbol.search

對象的 Symbol.search屬性,指向一個方法,當該對象被 String.prototype.search 方法調用時,會返回該方法的值

8.7. Symbol.split

對象調用Symbol.split屬性,指向一個方法,當該對象被 String.prototype.split 方法調用時,會返回該方法的值。

8.8. Symbol.iterator

對象的 Symbol.iterator屬性,指向該對象默認的遍歷器方法

8.9 Symbol.toPrimitive

對象的Symbol.toPrimitive屬性,指向一個方法。該對象被轉爲原始類型的值時,會調用這個方法,返回該對象對應的原始類型值。Symbol.toPrimitive被調用時,會接受一個字符串參數,表示當前運算的模式,一共有三種模式。

  • Number:該場合須要轉成數值
  • String:該場合須要轉成字符串
  • Default:該場合能夠轉成數值,也能夠轉成字符串

8.10 Symbol.toStringTag

對象的Symbol.toStringTag屬性,指向一個方法。在該對象上面調用Object.prototype.toString方法時,若是這個屬性存在,它的返回值會出如今toString方法返回的字符串之中,表示對象的類型。也就是說,這個屬性能夠用來定製[object Object]或[object Array]中object後面的那個字符串。

8.11. Symbol.unscopables

對象的 Symbol.unscopables 屬性,指向一個對象,該對象指定了使用 with 關鍵字時,哪些屬性會被 with 環境排除。

歡迎關注 公衆號【小夭同窗】

重學js系列

重學js之JavaScript簡介

重學 JS 之在 HTML 中使用 JavaScript

重學js之JavaScript基本概念(上)=> 數據類型

重學js之JavaScript基本概念(中)=> 操做符

重學js之JavaScript基本概念(下)=> 運算符

ES6入門系列

ES6入門之let、cont

ES6入門之變量的解構賦值

ES6入門之字符串的擴展

ES6入門之正則的擴展

ES6入門之數值的擴展

ES6入門之函數的擴展

ES6入門之數組的擴展

ES6入門之對象的擴展

ES6入門之對象的新增方法

Git教程

前端Git基礎教程

相關文章
相關標籤/搜索