探祕JS的所謂的第七種資料型別Symbol
因為在2007年之前Js給予我們typeof解析資料型別的一共有六種(一直有爭議,但是我們暫時就按typeof來算)
'function'
'Number'
'Object'
'boolean'
'String'
'undefined'
當我們去 typeof Symbol()的時候會驚奇的發現 返回了一個‘symbol’ ;
首先肯定要有疑問,這貨是啥?當然第一種想法其實就是肯定很強大。因為前六種已經強大的一種地步了,這貨肯定也一定非常強大。
首先我們先帶著我們的好奇心一步一步來看看這個鬼東西...
首先先驗證一下它是不是物件。
通過我先說一下我對物件研究的他有三種機制:
1.只要是物件就可以引用。
2.只要是物件都可以賦予私有屬性。
3.物件都不相等。
那麼
var a = Symbol();
a.b = 10;//賦予私有屬性
a.b // undefined
那麼看來這貨不是個物件 ,既然不是物件我們來看看它的一些別的特性.
首先在api上Symbol提供了兩個方法第一個是for 另外一個是keyFor
var s1 = Symbol.for('abc');
var s2 = Symbol.for('abc');
Symbol() === Symbol() //false
s1 === s2 //true
Symbol.keyFor(s1)// 'abc'
當然這兩個看起來比較容易 似乎就是一個賦予一個值然後就會把原來的值吐出來,當然真是原來的值麼?帶著這樣的疑問我又繼續做了一些實驗。
var s1 = Symbol.for([1,2,3]);
Symbol.keyFor(s1); // "1,2,3" 字串的 1,2,3
var s1 = Symbol.for(function (){});
Symbol.keyFor(s1); "function (){}" 字串的fn;
你會發現這貨你存的東西只會以字串的東西吐出來。
當然這個東西官方說由於每一個Symbol值都是不相等的,這意味著Symbol值可以作為識別符號,用於物件的屬性名,就能保證不會出現同名的屬性。這對於一個物件由多個模組構成的情況非常有用,能防止某一個鍵被不小心改寫或覆蓋。
也就是說可以作為存在json 中讓key 永遠不相等.ok,那麼就完全可以這樣..
var a = {};
a[Symbol()]= 'Hello!';
a[Symbol()]= 'Hello!';
a[Symbol()]= 'Hello!';
console.log(a);
Object
Symbol(): "Hello!"
Symbol(): "Hello!"
Symbol(): "Hello!"
__proto__: Object
你會發現出現了連續的三個 a的屬性 都是hello 並且沒有覆蓋 。也就是說這麼寫的話可以有效的防止其json重名問題,不過拿起來就非常費勁了.
for(var i in a){
console.log(i +',' +a) //沒有任何的東西
}
當然這就比較可以可疑了 json 用symbol 存上東西了 但是又用for in 拿不到 也就說如果直接這麼賦值 json認但是for in 迴圈不認.而且咱們也拿不到。
但是換一種方式就沒問題了。用變數存的話,雖然雖然for in 拿不到 但是咱們可以拿到值。
var a = Symbol('aaa');
b = {};
b[a] = 10 ;
console.log(b[a])//10 輕鬆拿到值
其實不難看出來 Symbol對for in不是很友好,但是 對json 很友好。
這時如果使用別的方法拿值呢?顧名思義,Object.getOwnPropertyNames()是拿物件私有屬性的的方法。我們來試試。
let b = {};
b[Symbol()]=10;
b[Symbol()]=15;
Object. getOwnPropertyNames(b) //[] 可以理解為 其實 Symbol不作為b的私有屬性存在。
當然能不能拿到呢?其實也能拿到。
他提供了一個getOwnPropertySymbols方法可以讓我找到存在記憶體裡的Symbol;
例如:
let a = {};
a[Symbol()]=10;
a[Symbol()]=15;
Object.getOwnPropertySymbols(a) //[Symbol(),Symbol()] //這裡面以陣列的形式返回了 咱們使用的兩個Symbol();
Object.getOwnPropertySymbols(a)[0]//Symbol() 第一個Symbol()
a[Object.getOwnPropertySymbols(a)[0]]//10 拿到存在的這個值。
其實知道是陣列後 我們就可以迴圈 obj.getOwnPropertySymbols(a) 這個東西 然後輸出值了。其實說回來只是換了一種方法拿值,存值。而這種方法更安全更隱蔽而已。
而Symbol還有一些比較特殊的特性。
js中的~(按位非) 是一個比較強勢的轉換number的東西。例如:
~NaN //-1
~function (){}//-1
~undefined //-1
var a = function (){};
~a() //-1
~new a() //-1
基本任何東西都能轉成number,而
~Symbol //-1
~Symbol() //報錯 似乎說明了 其跟function 有著本質的區別;
另外呢,Symbol值不能與其他型別的值進行運算,會報錯。
var sym = Symbol('My symbol');
"your symbol is " + sym // TypeError: can't convert symbol to string es5之前的報錯
`your symbol is ${sym}` // TypeError: can't convert symbol to string es6字串照樣的報錯
另外,Symbol值也可以轉為布林值,但是不能轉為數值。
var sym = Symbol();
Boolean(sym) // true
!sym // false
Number(sym) // TypeError
sym + 2 // TypeError
這些都是Symbol的一些小特性。
其實來說Symbol作為一個新的資料型別 最強的而不是幹以上的這些事而是一些配合原型方法的一些開關,可以強化方法的使用。
比如說 Symbol.isConcatSpreadable 這個方法
咱們都知道 正常的陣列concat方法是連線字串 。
let arr = ['c', 'd'];
['a', 'b'].concat(arr2,'e') //['a','b','c','d','e'];
而我們一旦把開關開啟後會發現一些意想不到的結果;
let arr2 = ['c', 'd'];
arr2[Symbol.isConcatSpreadable] = false;
['a', 'b'].concat(arr2, 'e') //['a','b',['c','d'],'e']//會發現以陣列的形式插入到裡面了。
當然他還包括了一些別的方法例如 他可以測試ES6 新增的內建物件方法 Symbol.toStringTag。
JSON[Symbol.toStringTag]:'JSON'
Math[Symbol.toStringTag]:'Math'
Module物件M[Symbol.toStringTag]:'Module'
ArrayBuffer.prototype[Symbol.toStringTag]:'ArrayBuffer'
DataView.prototype[Symbol.toStringTag]:'DataView'
Map.prototype[Symbol.toStringTag]:'Map'
Promise.prototype[Symbol.toStringTag]:'Promise'
Set.prototype[Symbol.toStringTag]:'Set'
%TypedArray%.prototype[Symbol.toStringTag]:'Uint8Array'等
WeakMap.prototype[Symbol.toStringTag]:'WeakMap'
WeakSet.prototype[Symbol.toStringTag]:'WeakSet'
%MapIteratorPrototype%[Symbol.toStringTag]:'Map Iterator'
%SetIteratorPrototype%[Symbol.toStringTag]:'Set Iterator'
%StringIteratorPrototype%[Symbol.toStringTag]:'String Iterator'
Symbol.prototype[Symbol.toStringTag]:'Symbol'
Generator.prototype[Symbol.toStringTag]:'Generator'
GeneratorFunction.prototype[Symbol.toStringTag]:'GeneratorFunction'
不過其實用ES5之前的方法依然也可以檢驗出來內建物件,所以Symbol就是更規範化而已,就用map舉例:
Object.prototype.toString.call(new Map())//'[object Map]'
別的內建物件也是同理。
Symbol.unscopables 也是Symbol一個比較有意思的東西。可以找到物件內哪些屬性被with排除。
Object.keys(Array.prototype[Symbol.unscopables])//['copyWithin', 'entries', 'fill', 'find', 'findIndex', 'keys'] 以陣列的形式返回 也就是說 這些屬性會被with排除。
其實這些只是Smybol的冰山一角,更多的是Symbol是服務於ES6中。
讓我們繼續慢慢探索好了。