12月初的时候,我打算将阮大大的《ECMAScript6 入门》作一个笔记,顺带做为参考的,可是这个文章已经在11号左右的时候流产了。为何呢?一个是由于最近主管给分配了任务,没啥时间作;还有一个缘由就是:尼玛看不懂了啊!!!javascript
最近事情少了,回过头继续学习,看到一个有意思的玩意:Symbol
。java
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。es6
原始数据类型??这让我想起来以前的一道笔试题:JavaScript数据类型有哪些?面试
当时我写的是:undefined、null、number、string、boolean、Object。面试官问了我一句,数据类型就是这些了吗?我内心有个Symbol
的印象,可是当面试官继续问做用的时候我哑然了,由于个人确没了解过具体这东西是干吗的呀?因而面试官给我讲起了Symbol的相关知识:数组
ES5 的对象属性名都是字符串,这容易形成属性名的冲突。好比,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。若是有一种机制,保证每一个属性的名字都是独一无二的就行了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的缘由。函数
let s = Symbol();
typeof s // => "symbol"
复制代码
这好像和之前生成对象的写法不太同样,没有用new
命令,你问我为何?废话用new
命令那不就是对象了吗?Symbol
是原始数据类型呀!和对象是并列关系~学习
有的童鞋会问,用Symbol()
生成的话,很不利于阅读呀,谁知道这个Symbol是干吗的呢?不要紧,Symbol已经帮咱们想好了解决办法:ui
Symbol函数能够接受一个字符串做为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。this
let s1 = Symbol('s1'); // => Symbol(s1)
let s2 = Symbol('s2'); // => Symbol(s2)
let s3 = Symbol('s1'); // => Symbol(s1)
复制代码
这时候又有好奇宝宝要问了,加上了描述,这两个Symbol值是否是就变成同样的了?spa
s1 == s3 // => false
s1 === s3 // => false
复制代码
不论是==
仍是===
,返回值都是false,这说明:
Symbol函数的参数只是表示对当前 Symbol 值的描述,所以相同参数的Symbol函数的返回值是不相等的。
Symbol 值不能与其余类型的值进行运算,会报错。
let s = Symbol('s');
s + ' is a Symbol!' // => index.js:13 Uncaught TypeError: Cannot convert a Symbol value to a string
复制代码
Symbol 值能够显式转为字符串。
let s = Symbol('s');
s.toString(); // => Symbol(s)
s.toString() + ' is a Symbol!' // => Symbol(s) is a Symbol!
复制代码
能够看到这时候能够和字符串相加了。
Symbol 值也能够转为布尔值,可是不能转为数值。
let s = Symbol();
Boolean(s); // => true
!s // => false
Number(s); // => TypeError: Cannot convert a Symbol value to a number
Number(s) + 2; // => TypeError: Cannot convert a Symbol value to a number
复制代码
ES2019 提供了一个实例属性description,直接返回 Symbol 的描述。
let s = Symbol('this is a Symbol');
s.description // => this is a Symbol
复制代码
Symbol 值能够做为标识符,用于对象的属性名。
有人问了,我用字符串作属性名,又快又方便,为何要用Symbol呢?
这个问题问的好,不少学习不细心(可能只有我一个)的童鞋就有这样的疑问!
咱们来看看上面对Symbol的介绍:
ES6 引入了一种新的原始数据类型Symbol,表示
独一无二
的值。
是的,独一无二,你有没有在开发中遇到变量名相似的状况,苦于不知道怎么区分?你是否在操做其余同事编写的对象的时候,惧怕本身添加的变量名将其余变量覆盖,或者被其余变量覆盖呢?这时候你就须要一个Symbol值来续命(+ 1s)了!
let s = Symbol();
// 第一种写法
let obj = {
[s]: 'hello Symbol'
}
// 第二种写法
let obj = {};
obj[s] = 'hello Symbol'
// 第三种写法
let obj = {};
Object.defineProperty(obj,s,{value:'hello Symbol'})
// 以上三种写法的结果都是
obj[s]; // => hello Symbol
复制代码
注意
Symbol 做为属性名,遍历对象的时候,该属性不会出如今for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。
for大法都没法遍历?那是否是私有属性?回答是:NO,对象提供了一个Object.getOwnPropertySymbols()
方法用于获取对象内全部Symbol属性名,返回一个数组
有时,咱们但愿从新使用同一个 Symbol 值,Symbol.for()方法能够作到这一点。
let s1 = Symbol.for('s');
let s2 = Symbol.for('s');
s1 === s2; // => true
复制代码
那么问题来了:Symbol
和Symbol.for
的区别是什么呢?
前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,若是不存在才会新建一个值。
到这个时候面试已经结束
但我感受事情可能并无想象中的那么简单
回看一下阮大大的教程最后:
除了定义本身使用的 Symbol 值之外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。
对象的Symbol.hasInstance属性,指向一个内部方法。当其余对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法。
function fn(){
}
let ins = new fn();
ins instanceof fn; // => true
fn[Symbol.hasInstance](ins); // => true
复制代码
对象的Symbol.isConcatSpreadable属性等于一个布尔值,表示该对象用于Array.prototype.concat()时,是否能够展开。
let arr1 = ['a','b'];
let arr2 = ['c','d'];
arr1.concat(arr2); // => ['a','b','c','d']
arr2[Symbol.isConcatSpreadable] = false;
arr1.concat(arr2); // => ['a','b',['c','d']]
复制代码
这些咱也不知道有啥用,咱也看不懂,等后面有闲工夫再去研究吧~