天天看點

ES6中Symbol的使用

1. 基礎使用

1.1 Symbol概述

// ES6新引入的原始資料類型,表示獨一無二的值,是類似于字元串的資料類型。
//通過 Symbol函數生成 可作為對象的屬性名
let s=Symbol();
console.log(s);        // Symbol()
console.log(typeof s); // Symbol

// Symbol不得使用new  因為Symbol不是對象 是原始類型的值
// 由于不是對象,是以也不能添加屬性
//這個括号中的參數是描述,用于區分Symbol,否則每個Symbol都一樣 不利于區分
let s1=Symbol('foo');
let s2=Symbol('bar');
console.log(s1);            //  Symbol(foo)
console.log(s2);            //  Symbol(bar)
console.log(s1.toString()); // "Symbol(foo)" 注意:控制台列印的是Symbol  但是其實是字元串形式

//若傳入的參數是一個對象 先調用對象的toString方法轉成字元串再生成Symbol對象,預設情況下對象的toString傳回的是"[object Object]"
const obj={
toString(){
    return 'abc'
   }
};
const sym=Symbol(obj);
console.log(sym);

//Symbol中 參數隻是描述,是以相同參數的傳回值還是不一樣的
//無參情況
let s3=Symbol();
let s4=Symbol();
console.log(s3 === s4);
//相同參數情況
let s5=Symbol('foo');
let s6=Symbol('foo');
console.log(s5 === s6);
//Symbol不可以和其他類型的值進行運算 但是可以轉成字元串 String(Symbol())
//Symbol可以轉換成布爾值 但是不能轉換成數值
           

1.2 Symbol作為屬性名

var mySymbol=Symbol();
//第一種寫法
var obj1={};
obj1[mySymbol]='hello';
//第二種寫法
var obj2={
  [mySymbol]:'hello'
}
//第三種寫法
var obj3={};
Object.defineProperty(obj3,mySymbol,{value:"hello"})
//結果都是 {Symbol(): "hello"}

//Symbol作為對象屬性名時不能使用點運算符。
var mySymbol2=Symbol();
var obj4={};
// 注意 使用 對象名.屬性名 的方式這裡的屬性名是字元串類型 
obj4.mySymbol2='hello';
console.log(obj4[mySymbol2]);    // undefined
console.log(obj4['mySymbol2']);  // hello

//在對象的内部中,使用Symbol定義屬性時,Symbol值必須放在[]中
let symbol=Symbol();
let obj={
  [symbol]:function () {
    return 1;
  },
  // [symbol](){return 1; } 簡寫
};
console.log(obj[symbol]());
           

1.3 Symbol作為屬性名如何周遊

//通過getOwnPropertySymbols擷取屬性名為Symbol類型的
let obj={};
let a=Symbol('a');
let b=Symbol('b');
obj[a]='hello';
obj[b]='world';
obj['c']='Symbol';
let objectSymbols=Object.getOwnPropertySymbols(obj);  
console.log(objectSymbols);    // 擷取的是數組 [ Symbol(a), Symbol(b) ] 

//Object.getOwnPropertySymbols()、for...in循環以及Object.getOwnPropertyNames對比
let obj2={};
let foo=Symbol('foo');
obj2[foo]='Symbol'
//for...in擷取不到Symbol的屬性的值
for (let i in obj2){
  console.log('這裡的代碼不會執行 因為for..in通路不到Symmbol的值');          //  不會執行 
}
//getOwnPropertyNames擷取不到Symbol屬性的值
console.log(Object.getOwnPropertyNames(obj2));    //  []
//Symbol屬性可以通過getOwnPropertySymbols擷取
console.log(Object.getOwnPropertySymbols(obj2));  //  [ Symbol(foo) ]

//可以通過新的API——Reflect.ownKeys()方法擷取所有鍵名
let obj3={
  [Symbol('symbol')]:'Symbol',
  str:'字元串',
};
console.log(Reflect.ownKeys(obj3));   //   [ 'str', Symbol(symbol) ]

//以Symbol值作為名稱的屬性不會被正常的方法周遊得到,可以利用這個特性定義一些非私有的又隻希望隻用于内部的方法
let serect=Symbol('serect');
class Test{
  [serect]='自定義私有屬性';
}

let t=new Test()
console.log(t.serect);   // 報錯 無法通過.的方式通路
console.log(t[serect]);  // 可以通路

// 使用Symbol作為屬性名 并不是真正的私有 而是不容易通路到 可以用作私有
           

1.4 Symbol.for()和Symbol.keyFor()的使用

// 剛剛提到了Symbol()即便參數相同 産生的Symbol的值也是不同的 那麼如果産生相同的值?
// 這時就要使用到Symbol.for()方法
// Symbol.for()方法可以建立的Symbol值是相同的,因為使用Symbol.for()建立時會被登記在全局環境中以供搜尋,而使用Symbol不會。
// Symbol.for()方法可以建立的Symbol值是相同的,因為使用Symbol.for()建立時會被登記在全局環境中以供搜尋,而使用Symbol不會。
let s1=Symbol.for('foo');
let s2=Symbol.for('foo');
console.log(s1 === s2);           // true
console.log(Symbol.for('bar') === Symbol.for('bar'));   // true 
console.log(Symbol('bar') === Symbol('bar'));           // false
let s3=Symbol('foo');            // 沒使用Symbol.for()  不在全局環境中
// Symbol.keyFor()隻能找到被Symbol.for()登記到全局環境中的值
console.log(Symbol.keyFor(s1));  // foo
console.log(Symbol.keyFor(s2));  // foo
console.log(Symbol.keyFor(s3));  // undefined
           

2. 内置的Symbol的值

内置的值還有很多 這裡簡要的介紹兩個比較好用的,具體想了解,可以看一下阮老師的書。 内置的 Symbol 值

2.1 Symbol.hasInstance

//Symbol.hasInstance屬性:指向一個内部方法,對象使用instanceof運算符時會調用這個方法,傳回Symbol.hasInstance指向的内部方法的值。若沒有自定義Symbol.hasInstance  預設傳回的是 判斷該對象是否為某個構造函數的執行個體。

//mc instanceof MyClass就會運作 MyClass[Symbol.hasInstance](foo)方法
let test={};
// 判斷 test是否是Object的執行個體對象
console.log(Object[Symbol.hasInstance](test));  // true
// 判斷 test是否是String的執行個體對象
console.log(String[Symbol.hasInstance](test));  // false

// 例一:自定義[Symbol.hasInstance](){}方法
class MyClass{
  constructor(){
  }
  static [Symbol.hasInstance](foo){
    // 這個傳回的值 就是使用instanceof的結果 
    return foo.constructor.name===MyClass.name;
  }
}
let mc=new MyClass();
console.log(mc instanceof MyClass);  // true 
// 例二:自定義[Symbol.hasInstance](){}方法  判斷是否為偶數
class Even{
  static [Symbol.hasInstance](obj){
    // 這個傳回的值 就是使用instanceof的結果 
    return Number(obj)%2===0;
  }
}
console.log(1 instanceof Even);  // false
console.log(2 instanceof Even);  // true 
           

2.2 Symbol.isConcatSpreadable

// 對象的Symbol.isConcatSpreadable屬性等于一個布爾值,表示該對象使用Array.prototype.concat()時是否可以展開。
// 預設情況下,Symbol.isConcatSpreadable都是undefined 
// 數組預設就可以展開
let arr1=['c','d'];
console.log(arr1[Symbol.isConcatSpreadable]);   // undefined,因為是數組 是以可以展開
let arr2=['a','b'].concat(arr1,'e');
console.log(arr2);           // [ 'a', 'b', 'c', 'd', 'e' ] 使用concat方法繼續拼接

// 類似數組結構的對象,需要将isConcatSpreadable設定為true方可展開
// 不是類似數組結構的對象 設定為true 也沒有用
let obj={0:'a',1:'b',length:2};          // 這就是類似數組的對象
let arr3=['c','d'].concat(obj,'e');      // 不将isConcatSpreadable設定為true 并進行拼接
console.log(arr3);                       // ['c','d',{ '0': 'a', '1': 'b', length: 2 },'e'] 可以看到obj還是以對象的形式展示
obj[Symbol.isConcatSpreadable]=true;     // 将isConcatSpreadable設定為true 
let arr4=['c','d'].concat(obj,'e');      // 進行拼接
console.log(arr4);                       // ['c','d','a','b','e'] 對象obj除length之外的值都被拆分出來進行拼接了
           

繼續閱讀