旭哥推荐: 打开 Coding.net https://coding.net/git# 填写邀请码 ZXXZXX , 新老用户领黄金会员赢现金红包! 体验极速代码托管服务!
一, 路还很远, 慢慢走一点一点
JS 的学习停滞了很久, 当想要重新拾起的时候, 发现已经拉了很多东西了.
然而, 自己并不着急. 朝花因故未采撷, 夕拾依旧烂漫天. 从小处开始, 一点一点上下求索, 漫漫长路就会全然走在脚下.
例如, 从 ES6 一个名不见经传的 Symbol()方法说起.
//zxx: 本文部分内容参考自 MDN 上关于 Symbol https://developer.mozilla.org/en-US/docs/web/JavaScript/Reference/Global_Objects/Symbol 的文档.
二, 关于 symbol 这个词
Symbol 这个词在 IT 软件领域实际上是个常见角色, 在传统前端技术领域, 这个词出现频率有限, 但是, 随着现代 web 技术的发展, Symbol 这个词开始在不同前端语言中出现.
symbol 的中文意思是: 符号; 象征; 标志; 记号等.
我最早知道是在 Adobe Illustrator 软件中, 作为一个矢量符号存在. 在 SVG 中, 就有专门的 <symbol> 标签, 用来指代 SVG 小图标.
在这里, Symbol 又有了另外的角色, 作为一个 JavaScript 的原生数据类型 (primitive data type) 存在.
说到 JavaScript 原生数据类型, 我们通常想到的有这 6 种: undefined,null,boolean,string,number,object. 而 symbol 是 ES6 新增的一个原生数据类型.
而 Symbol 本身又是一个方法.
例如下面 JS:
typeof Symbol();
可以看到 Symbol()作为一个方法执行了, 同时 typeof 其类型, 也是'symbol', 如下截图:
但 Symbol 不能作为构造函数使用, 也就是下面这样是会嗝屁的!
new Symbol();
三, Symbol 的作用是什么?
Symbol 的作用非常的专一, 换句话说其设计出来就只有一个目的 -- 作为对象属性的唯一标识符, 防止对象属性冲突发生.
举个例子, 你看上了公司前来的前台妹纸, 想了解关于她的更多信息, 于是就询问 Hr 同事, 扫地阿姨, 于是得到类似这样信息:
- let info1 = {
- name: '婷婷',
- age: 24,
- job: '公司前台',
- description: '平时喜欢做做瑜伽, 人家有男朋友, 你别指望了'
- }
- let info2 = {
- description: '这小姑娘挺好的, 挺热情的, 嘿嘿嘿......'
- }
显然, 你需要对这两个数据进行汇总, 结果, 就会发现, 描述都用了同一个对象属性 description, 于是整合的时候, 就容器冲突, 覆盖, 导致 "人家有男朋友" 这么重要的信息都没注意到.
但是, 如果要是 Symbol, 则完全就不要担心这个问题了:
- let info1 = {
- name: '婷婷',
- age: 24,
- job: '公司前台',
- [Symbol('description')]: '平时喜欢做做瑜伽, 人家有男朋友, 你别指望了'
- }
- let info2 = {
- [Symbol('description')]: '这小姑娘挺好的, 挺热情的, 嘿嘿嘿......'
- }
此时, 我们对 info1, info2 对象进行复制, 如下:
- let target = {};
- Object.assign(target, info1, info2);
此时 target 对象如下截图所示:
妹纸所有的描述信息都被完完整整地保留了下来了.
因为 Symbol()返回值是唯一的, 也就是:
Symbol('description') === Symbol('description'); // 返回值是 false
四, Symbol()的语法
语法如下:
Symbol([description])
其中 description 为可选参数, 字符串, 没什么特别的作用, 就是 debug 调试的时候可以用来作为标记.
如何获取 Symbol()对应属性值?
拿上面 target 举例, 如何获得对妹纸的 description 描述信息呢?
我们可以使用
Object.getOwnPropertySymbols(obj)
这个方法进行获取, 可以返回 obj 对象中的 Symbol 信息, 例如:
Object.getOwnPropertySymbols(target);
妹纸的描述信息就出现了, 如下截图:
Symbol 在和对象使用的时候, 往往离不开 JS 中的数组括号[], 例如:
- var smy = Symbol();
- var info = {
- smy: 'x',
- [smy]: 'y'
- };
此时:
- console.log(info.smy); // 输出'x'
- console.log(info['smy']); // 输出'x'
- console.log(info[smy]); // 输出'y'
五, Symbol 其他一些知识
1. Symbol 与运算, 类型转换等
symbol 值虽然不是对象, 但是根据自己测试, 在 Chrome 和 Firefox 下都是可以添加属性的, 例如:
- var smy = Symbol();
- smy.description = '描述';
更新于翌日
感谢 yyhaxx 的反馈, 在 macos chrome 65.0.3325.181 和 node v8.9.4 中测试给 symbol 加属性结果是 undefined, 与上面 window PC 测试结果有些细节差异.
但是在类型转换时候 symbol 值会遇到不少局限:
Symbol 值可以显式转为字符串, 也可以转为布尔值, 但是不能转为数值. 例如:+sym 会报错, 如下图:
隐式地创建一个新的 string 类型的属性名也会报错, 例如
Symbol("foo") + "bar"
将抛出一个 TypeError:
使用宽松相等时, Object(sym) == sym 返回值是 true. 注意这里外面套的是 Object();
2. Symbol 与 for...in 迭代
Symbols 在 for...in 迭代中不可枚举, 如果想要达到效果, 借助
Object.getOwnPropertySymbols(obj)
这个方法.
- var obj = {};
- obj[Symbol("a")] = "a";
- obj[Symbol.for("b")] = "b";
- obj["c"] = "c";
- obj.d = "d";
- for (var i in obj) {
- console.log(i); // 输出 "c" 和 "d"
- }
3. Symbol 与 JSON.stringify()
当使用 JSON.strIngify()时以 symbol 值作为键的属性会被完全忽略, 示意代码:
JSON.stringify({[Symbol("foo")]: "foo"}); // '{}'
4. Symbol 包装器对象作为属性的键
围绕原始数据类型创建一个显式包装器对象从 ECMAScript 6 开始不再被支持, 所以 new Symbol()会报错, 然而, 现有的原始包装器对象, 如 new Boolean,new String 以及 new Number 因为遗留原因仍可被创建.
此时, 如果我们想创建一个 Symbol 包装器对象 (Symbol wrapper object), 你可以使用 Object()函数:
- var sym = Symbol("foo");
- typeof sym; // "symbol"
- var symObj = Object(sym);
- typeof symObj; // "object"
当一个 Symbol 包装器对象作为一个属性的键时, 这个对象将被强制转换为它包装过的 symbol 值:
- var sym = Symbol("foo");
- var obj = {[sym]: 1};
- obj[sym]; // 1
- obj[Object(sym)]; // 还是 1
六, 结束语
实用角度讲, 上面关于 Symbol()的知识已经足够了, 实际上文档中虽然还展示了很多其他 API 和方法, 例如 Symbol 在全局有个注册表, 它为字符串和 Symbol 提供了一对一的关系, 这种对应关系甚至是跨 service worker 和 iframe 的, 可以通过使用 Symbol.for(key)返回 Symbol.
不过文中并没有详细介绍, 因为本文标题是 "简单介绍", 哈哈, 开个玩笑, 实际上考虑的是投资收益比, 如果我们追求的不是成为在很高技术造诣的牛人, 当下这个阶段, 其实可以不必花太多时间过于深究, 面向业务面向项目, 收益要更大些.
好, 就这些, 真. 简单介绍.
ECMAScript 6 相关知识我自己也处于婴儿学步阶段, 如果有表述不准确的地方, 欢迎大力指正!
来源: https://juejin.im/entry/5ad2acc551882548fe4a900a