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之外的值都被拆分出來進行拼接了