首先, 为什么说叫所谓呢?
因为在 2007 年之前 JS 给予我们 typeof 解析数据类型的一共有六种(一直有争议, 但是我们暂时就按 typeof 来算)
- 'function'
- 'Number'
- 'Object'
- 'boolean'
- 'String'
- 'undefined'
但当我们去 typeof Symbol () 的时候, 会惊奇的发现, 返回了一个
'symbol'
首先肯定要有疑问, 这货是啥?
当然第一种想法其实就是肯定很强大. 因为前六种已经强大的一种地步了, 这货肯定也一定非常强大.
首先我们先带着我们的好奇心一步一步来看看这个鬼东西.
首先先验证一下它是不是对象.
通过我先说一下我对对象研究的他有三种机制:
只要是对象就可以引用.
只要是对象都可以赋予私有属性.
对象都不相等.
那么
- 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[i]) // 没有任何的东西
- }
- ```
当然这就比较可以可疑了, 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 值也可以转为布尔值, 但是不能转为数值. 这些都是 Symbol 的一些小特性.
- var sym = Symbol();
- Boolean(sym) // true
- !sym // false
- Number(sym) // TypeError
- sym + 2 // TypeError
其实来说 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 中. 让我们继续慢慢探索好了.
# 总结
** 如果你觉得这篇文章不错, 请别忘记点个 ` 赞 ` 跟 ` 关注 ` 哦~**
** 更多学习内容请阅读我的知乎专栏: [打造全网 web 高级前端工程师资料库 (总目录) 看完学的更加快, 知识更牢固. 你值得拥有(持续更新)~](https://zhuanlan.zhihu.com/p/148787766)**
来源: http://www.jianshu.com/p/7901517d3840