ES6躬行记(6)——Symbol
本节将会重点分析ES6引入的第6种基本类型:Symbol(符号)。符号可以像字符串那样作为对象的属性名,只是它有唯一性的特点,可以避免属性名之间的冲突。
一、创建
符号没有字面量形式,只能通过Symbol()函数创建。该函数有一个可选的参数,只是用来描述当前符号,除了便于阅读之外,没有其他用途。由此可知,即使两个符号的描述相同,它们还是不能画等号。注意,Symbol()不是构造函数,因此不能和new运算符组合使用,否则会抛出类型错误。下面用一个例子展示符号的创建。
var sym1 = Symbol(), sym2 = Symbol("name"), sym3 = Symbol("name"), sym4 = new Symbol(); //抛出类型错误 console.log(sym2 === sym3); //false
如果要识别一个变量是否为符号,可以用typeof运算符。ES6扩展了它,当检测到符号时,能返回一个新的类型字符串“symbol”,具体如下所示。
typeof sym1; //"symbol" typeof sym2; //"symbol"
二、类型转换
符号在类型转换时表现得并不灵活,它无法与数字或字符串进行运算,也无法显式的转换成数字。如下所示,后面四条语句在执行时都会报错。
var sym = Symbol("age"); Number(sym); parseInt(sym); 1 + sym; "" + sym;
不过,符号可以显式的转换成字符串或布尔值,具体如下所示。
Boolean(sym); //true !sym; //false sym.toString(); //"Symbol(age)" String(sym); //"Symbol(age)"
三、全局共享
ES6会在内部维护一张全局符号注册表,通过Symbol.for()方法,可以登记指定符号,使其变成一个全局有效地符号,从而达到全局共享。该方法只接收一个参数,这个参数既是注册表中的键值,同时也是此符号的描述。下面的代码调用了两次Symbol.for()方法,传递了相同的参数,返回的却是同一个全局符号。
var sym1 = Symbol.for("name"), sym2 = Symbol.for("name"); console.log(sym1 === sym2); //true
在上面的代码中,第一次调用Symbol.for()方法时,会在注册表中搜索键值为“name”的符号,由于没有找到,所以就会创建一个新的符号。而在第二次调用Symbol.for()方法时,由于传递的键值与前一次相同,因此会返回刚刚的那个符号。从而可知,对变量sym1和sym2进行全等比较,返回的结果将是true。
如果要获取某个全局符号所对应的键值(即它的描述),那么可以通过Symbol.keyFor()实现,具体操作如下所示。
Symbol.keyFor(sym1); //"name" Symbol.keyFor(sym2); //"name"
四、属性名
本节开头曾提到过对象的属性名可以用符号表示,而这类属性可以有三种赋值方式。第一种是用方括号,注意,不能用点号,因为点号后面的标识符会被识别成字符串而不是符号。下面代码分别用方括号和点号为obj对象的sym属性赋值,前者被识别成了符号属性,而后者却被识别成了字符串属性。
var sym = Symbol("name"), obj = {}; obj[sym] = "strick"; obj.sym = "strick"; console.log(obj); //{Symbol(name): "strick", sym: "strick"}
第二种是在创建对象字面量时,用计算属性名的方式(即属性名被方括号包裹)为其赋值,如下所示。
obj = { [sym]: "freedom" };
第三种是调用Object.defineProperty()或Object.defineProperties()方法来为符号属性赋值,如下所示。
Object.defineProperty(obj, sym, { value: "justice" });
注意,符号属性是不可枚举的,既不能被for-in等循环遍历到,也不能被Object.keys()、Object.getOwnPropertyNames()等方法读取到。但可以通过Object.getOwnPropertySymbols()方法获得对象的符号属性,具体如下所示。
obj = { [sym]: "freedom", age: 28 }; Object.keys(obj); //["age"] Object.getOwnPropertyNames(obj); //["age"] Object.getOwnPropertySymbols(obj); //[Symbol(name)]
五、内置符号
ES6提供了一些内置符号,也叫做知名符号(Well-Known Symbol)。它们暴露了语言的内部逻辑,允许开发人员修改或拓展规范所描述的对象特征或行为。每一个内置符号对应Symbol对象的一个属性,例如Symbol.hasInstance、Symbol.iterator等,表1列出了11个内置符号。
表1 内置符号
属性名称 | 值类型 | 描述 |
hasInstance | 方法 | 当使用instanceof运算符时会调用该方法 |
isConcatSpreadable | 布尔值 | 当对象作为Array.prototype.concat()方法的参数时,控制该对象是否被展开 |
iterator | 方法 | 返回一个迭代器,用于定义一个可迭代的对象 |
match | 方法 | 当对象作为String.prototype.match()方法的参数时,该方法会被调用 |
replace | 方法 | 当对象作为String.prototype.replace()方法的参数时,该方法会被调用 |
search | 方法 | 当对象作为String.prototype.search()方法的参数时,该方法会被调用 |
split | 方法 | 当对象作为String.prototype.split()方法的参数时,该方法会被调用 |
species | 方法 | 创建派生类的构造函数 |
toPrimitive | 方法 | 当对象需要转换成原始值(即执行ToPrimitive抽象操作)时,该方法会被调用 |
toStringTag | 字符串 | 指定对象的类型,可在调用Object.prototype.toString()方法的时候返回 |
unscopables | 对象 | 保存在这个对象中的属性将不能被with语句所引用 |
下面会给出4个内置符号的示例,分别是hasInstance、isConcatSpreadable、match和toStringTag。
let digit = { [Symbol.hasInstance](number) { return !(number % 2); //判断数字是否为偶数 } }; 1 instanceof digit; //false 2 instanceof digit; //true let arr1 = [3, 4]; [1, 2].concat(arr1); //[1, 2, 3, 4] let arr2 = [3, 4]; arr2[Symbol.isConcatSpreadable] = false; //禁止展开 [1, 2].concat(arr2); //[1, 2, [3, 4]] let regex = { [Symbol.match](str) { return str.substr(0, 10); } }, message = "My name is strick"; message.match(regex); //"My name is" let people = { [Symbol.toStringTag]: "People" }; people.toString(); //"[object People]"
相关文章
- 记录一次成功CICD完整亲身实践从此踏进入Devops大门
- 手写Mybatis和Spring整合简单版示例窥探Spring的强大扩展能力
- 云原生时代之Kubernetes容器编排初步探索及部署、使用实战-v1.22
- 走进Spring Boot源码学习之路和浅谈入门v2.5.3
- 从Redis分布式缓存实战入手到底层原理分析、面面俱到覆盖大厂面试考点
- 浅谈MySQL数据库面试必要掌握知识点
- Apache Kafka分布式流处理平台及大厂面试宝典v3.0.0
- Apache RocketMQ分布式消息传递和流数据平台及大厂面试宝典v4.9.2
- Netty高性能网络应用框架对标P7面试题分享v4.1.70.Final
- 企业数据治理落地和同行面试基础
- Apache ZooKeeper原理剖析及分布式理论名企高频面试v3.7.0
- 从ApacheTomcat架构谈面试到源码编译环境v10.0.12
- MySQL数据库之大厂面试必备技能v8.0.27
- 数据安全技术落地经验浅谈和分类分级实施
- Redis分布式缓存剖析及大厂面试精髓v6.2.6
- Nginx后端开发人员必学神器-并发编程经典之作剖析和名企热点面试v1.21.3
- 并发编程热身十个性能小面试题
- 数字化大时代崛起的数据安全能力框架
- Kubernetes容器编排探索与实践v1.22.1-上半部分
- 数据安全分类分级剖析