Javascript Symbol 藏匿的將來之星

ES6中基礎範例增添到了7種,比上一個版本多了一個Symbol,貌似湧現了很長時刻,但卻因沒有運用場景,一向看成一個觀點層來明白它,我想,用它的最好的體式格局,照樣要主動的去深切相識它吧,所以我從基礎部分和總結的有用場景來剖析這個特徵。已相識運用要領或許時刻緊急者能夠從有用場景一節最先瀏覽

base

起首,它給我的第一以為就是ES6做出了許多開釋言語特徵方面的轉變,它能讓我們越發相識言語內部機制,Symbol以對象的鍵值定義,比方

 let key = Symbol('test');  
 let obj = {};
 obj[key] = 'alone';
 obj[key];  // "alone"

Symbol正如其名,示意一個唯一的標示,以屬性的體式格局存在於對象當中,它吸收一個參數,沒有本質的作用,只是為了做一個形貌。以上我們經由過程直接量的體式格局來定義它,而且取值時,也須要運用key舉行讀取,假如湧現跨作用域的狀況,是不是是就不能獵取了?

function sent(key){
    accept({[key]:"2018"})
}
function accept(obj) {
   obj[???] //我怎樣拌?
}

以上兩個作用域中,假如不把key通報過來,是沒法讀取的,一個屬性還好,然則假如多了,那末靠參數通報key是不現實的. 在這類狀況下,我們能夠運用 Symbol.for 來為它再增加一個標示,它接收一個參數String{key}。一般,它做為一個偏功用性的標記來示意,在全劇中它是唯一的。

function sent(key){
    return accept({[key]:"2018"},key)
}
function accept(obj,key) {
   console.log(Symbol.keyFor(key))  //CURRENT_YEAR
   return obj[Symbol.for(Symbol.keyFor(key))] //CURRENT_YEAR
}
sent(Symbol.for('CURRENT_YEAR'))

而且運用 Symbol.for 來天生,會在存入當前全局高低文中一個<List>構造中,我們稱它為GlobalSymbolRegistry , 望文生義,它是全局的,所以運用key時我們須要鄭重,尤其是在大型項目中。

須要還注重以下幾點:

  1. 讀取它須要運用 getOwnPropertySymbols 要領,詳細請參看MDN
  2. Symbol() !== Symbol() but Symbol.for(‘t’) === Symbol.for(‘t’)
  3. GlobalSymbolRegistry對象存在於當前窗口歷程中,直到封閉窗口,才清撤除

現在的瀏覽器版本中把Symbol打印出來是字符串的花樣,並沒有顯現詳細的對象構造,我們能夠直接打印 Symbol,來檢察對應的prototype屬性以及內部要領,所以
Symbol().__proto__ === Symbol.prototype

在運用 Symbol 做key值時,它閱歷了以下步驟

  1. 假如指向對象是沒有定義的則拋出範例毛病
  2. 假如形貌符為undefined則為”
  3. 把形貌符轉換為String花樣
  4. 天生唯一的key,並返回
  5. 末了一步,把這個key賦給對象,並以Symbol(des)的體式格局顯現,其內部照樣以key為準,所以 Symbol() !== Symbol() ,即使他們看起來都是 字符串的”Symbol()”

所以如許寫也是能夠的,然則貌似沒有什麼意義

var n = 1;
var key = Symbol('numer')
n[key] = ‘Symbol Number’

n[key]的時刻把n隱式轉換成封裝對象,併為他增加Symbol,但並沒有方法去經由過程封裝對象回訪這個Symbol

除了純真的用key之外,在Symbol類下另有一些有意義的要領,following :

iterator

為指向對象增加 iterator 接口,比方運用數組解構或許運用for of,它接收一個generator函數

class IteratorExec {
    constructor(){ this.count = 1 }
    *[Symbol.iterator] = function* (){
        yield this.count++;
        yield this.count++;
        yield this.count++;
    }
 }
let obj = new IteratorExec()
[...obj] //[1,2,3]

經由過程增加iterator運用數據解構,還能夠運用for of

 let values = [];
 for (let value of obj) { values.push(value) }
 values;  //[1,2,3]

注:ES6中Map,Set,數組和增加了Iterator接口的對象,具有Iterator接口.

asyncIterator

這不是ES6中的特徵,貌似放到了ES7中,能夠提早意淫一下以下代碼:

for await (const line of readLines(filePath)) {
  console.log(line);
}

toPrimitive

在對對象範例舉行轉換時,會舉行一次 toPrimitive,應用這個Symbol能夠轉變目的對象的轉換規則,轉變了之前的 “[object Object]”的牢固情勢

let obj = {
    [Symbol.toPrimitive](hint){
      switch(hint){
          case 'number': return 5;
          case 'string': return 'string';
          case 'default': return 'default'    
      }
    }
}
obj+11 // 'default11'
obj*2 // 10

這裏須要注重+ Number操縱是不屬於 ‘number’ 的,其他一般,如許就能夠定義轉對象範例的轉換規則了。

toStringTag

在javascript統統皆為對象,而在每一個對象中,都邑有一個內部屬性[[Class]]示意其對象範例,這在Symbol.toStringTag,中是能夠修正的,也就是說 ‘[object Object]’ 後邊的字符串是可自定義的

 let obj = {
    [Symbol.toStringTag]:'custom'
}
Object.prototype.toString(obj); // [object Object]
obj.toString();  //[object custom]

一般我們運用Object.prototype.toString讀取對象屬性,恰是由於向後兼容,範例在對象自身的toString上完成了這類特徵,而老式要領照舊運用。然則我們能夠運用以下體式格局:

obj = {
    [Symbol.toStringTag]:'custom'
    get [Symbol.toStringTag](){
        return 'custom'
    }
}
Object.prototype.toString.call(obj)

我們把obj傳入實行toString,能夠到達這類結果,能夠料想es6中,Object.toString是遭到高低文的影響的. 明顯,我們上面的兩個例子都是獵取的Object.prototype.toString 二者有很大區分,只要它才正確轉換,假如你的toString不全即是它,那是沒法轉換的,比方

 var n = new Number();
 n[Symbol.toStringTag] = 123;
 n.toString();  // “0” 

太稚子了,太無聊了?,Number私有的toString是直接把[[PrimitiveValue]]轉換成了字符串,這裏人人要萬萬留意,不要誤認為一切的對象增加了Symbol.toStringTag都能夠轉變,假如當前對象不是純對象,那末你能夠為此對象增加一個 getter 返回對應的範例,如許外部在運用Object…call的時,會獵取自定的範例。所以,這須要外部合營運用,你增加getter,人家不call你也是沒方法的。

別的Symbol暴露了幾種為原生對象定義了一些範例,比方

Math.toString();  //[object Math]

其他範例有 JSON, Promise, Map, TypedArray, DataView, ArrayBuffer, Genterator等等

unscopeables

const object1 = {
  property1: 42
};

object1[Symbol.unscopables] = {
  property1: true
};

with (object1) {
  console.log(property1);
}

這個功用我以為可用性為0,基礎不必,with就是據對制止的.

hasInstance

關於 instance運算符,為此操縱增加一個鈎子,第一參數是instance的左值,我們能夠返回true|false來定義運算符的返回值

var obj1 = {
    [Symbol.hasInstance](instance){
        return Array.isArray(Array)
    }
}
class Array1 {
  static [Symbol.hasInstance](instance) {
    return Array.isArray(instance);
  }
}
[] instance obj1  //true
console.log([] instanceof Array1);  //true

isConcatSpreadable

示意[].concat是不是能夠睜開,默許是true.

let arr = [1,2];
arr.concat([3,4],5)  //[1,2,3,4,5]

arr[Symbol.isConcatSpreadable] = false;
arr.concat([3,4],5)  //[[1,2],3,4,5]

// 也能夠把[3,4]提出來處置懲罰
let arr2 = [3,4]
arr2[Symbol.isConcatSpreadable] = false;
arr.concat(arr2,5); //[[1,2],[3,4],5]

只要在數組中這個symbol屬性為false,concat操縱時,就不會去解構。那末是不是是意味着屬性設置為ture,沒有意義了?關於數組來講是的,由於它默許就是true,但是關於類數組對象,它另有一個小功用:

// (續)
arr.concat({length:2,0:3,1:4,[Symbol.isConcatSpreadable]:true}) //[1,2,3,4]

match & replace & split & search

一些字符串的操縱要領,一同都說了,也許都一個意義,就是接收一個對象,然後完成一個鈎子處置懲罰的函數,並返回其處置懲罰結果,它們都是能夠吸收正則的要領,在ES6之前,假如我們須要對字符串有比較複雜的操縱基礎上都是在要領外部的,必

class MyMatch {
    [Symbol.match](string){return string.indexOf('world') }
}

'hello world'.match(new MyMatch()); //6

class MyReplace{
  [Symbol.replace](string) {
     return 'def'
  }
}

'abcdef'.replace(new MyReplace(),'xxx'); //'abcxxx'

class mySplit {
    [Symbol.split](val){
        return val.split('-');
    }
}

"123-123-123".split(new mySplit());  //['123','123','123']

class MySearch {
    constructor(value) {
        this.value = value;
    }
    [Symbol.search](string) {
        return string.indexOf(this.value);
    }
}
var fooSearch = 'foobar'.search(new MySearch('foo'));  //0
var barSearch = 'foobar'.search(new MySearch('bar'));  //3
var bazSearch = 'foobar'.search(new MySearch('baz'));  //-1

practice

  1. 能夠經由過程Symbol完成以上的功用性要領,比方增加 Iterator 接口,讓對象隊友接口特徵,現實開闢中,我預計很少會用到,卻是以為 sanycIterator 是將來的遠景,現在還在草案階段
  2. 關於Symbol做為鍵值的作用,很為難,現實開闢中,這個我也沒運用過,現在為止,只須要記着它有unique性,比方我們想要在一個對象中增加兩個一樣的key名,這類需求很不罕見,
var firstPerson = Symbol("peter");
var secondPerson = Symbol("peter");
var persons = {[firstPerson]:"first", [secondPerson]:"pan"};

總結

Symbol更多的是在運用和言語自身層面暴露更多的運用體式格局和特徵(on Object type),是的,它只以key的體式格局存在Object當中,在統統皆為對象中,它為 Next ECMScript Standard 供應了更多的可能性擴展性,這也是ES6中做的最大轉變方面之一,雖不經常使用但我們照樣要總結進修一下,以便在極度狀況下應變自若,假如有什麼文章中沒有涉及到的點,迎接補充! 注: 尤其是運用場景方面

    原文作者:一隻會飛的豬
    原文地址: https://segmentfault.com/a/1190000014538151
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞