简单了解ES6/ES2015 Symbol() 方法

  • A+
所属分类:JavaScript

by zhangxinxu from http://www.zhangxinxu.com/wordpress/?p=7473
本文可全文转载,但需保留原作者和出处。

一、路还很远,慢慢走一点一点

JS的学习停滞了很久,当想要重新拾起的时候,发现已经拉了很多东西了。

然而,自己并不着急。朝花因故未采撷,夕拾依旧烂漫天。从小处开始,一点一点上下求索,漫漫长路就会全然走在脚下。

例如,从ES6一个名不见经传的Symbol()方法说起。

//zxx: 本文部分内容参考自MDN上关于Symbol的文档。

二、关于symbol这个词

Symbol这个词在IT软件领域实际上是个常见角色,在传统前端技术领域,这个词出现频率有限,但是,随着现代web技术的发展,Symbol这个词开始在不同前端语言中出现。

symbol的中文意思是:符号;象征;标志;记号等。

我最早知道是在Adobe Illustrator软件中,作为一个矢量符号存在。在SVG中,就有专门的<symbol>标签,用来指代SVG小图标。

在这里,Symbol又有了另外的角色,作为一个JavaScript的原生数据类型(primitive data type)存在。

说到JavaScript原生数据类型,我们通常想到的有这6种:undefinednullbooleanstringnumberobject。而symbol是ES6新增的一个原生数据类型。

而Symbol本身又是一个方法。

例如下面JS:

typeof Symbol();

可以看到Symbol()作为一个方法执行了,同时typeof其类型,也是'symbol',如下截图:

简单了解ES6/ES2015 Symbol() 方法

Symbol不能作为构造函数使用,也就是下面这样是会嗝屁的!

new Symbol();

简单了解ES6/ES2015 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对象如下截图所示:

简单了解ES6/ES2015 Symbol() 方法

妹纸所有的描述信息都被完完整整地保留了下来了。

因为Symbol()返回值是唯一的,也就是:

Symbol('description') === Symbol('description');    // 返回值是false

四、Symbol()的语法

语法如下:

Symbol([description])

其中description为可选参数,字符串,没什么特别的作用,就是debug调试的时候可以用来作为标记。

如何获取Symbol()对应属性值?

拿上面target举例,如何获得对妹纸的description描述信息呢?

我们可以使用Object.getOwnPropertySymbols(obj)这个方法进行获取,可以返回obj对象中的Symbol信息,例如:

Object.getOwnPropertySymbols(target);

妹纸的描述信息就出现了,如下截图:

简单了解ES6/ES2015 Symbol() 方法

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 = '描述';

简单了解ES6/ES2015 Symbol() 方法

更新于翌日
感谢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 Booleannew 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

不过文中并没有详细介绍,因为本文标题是“简单介绍”,哈哈,开个玩笑,实际上考虑的是投资收益比,如果我们追求的不是成为在很高技术造诣的牛人,当下这个阶段,其实可以不必花太多时间过于深究,面向业务面向项目,收益要更大些。

weinxin
我的微信
爱生活、爱学习的小伙伴可以通过扫一扫二维码添加我的个人微信一起交流!
青青子衿

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: