写点什么

《看完就懂系列》答应我,看完就开始用 Symbol 好吗?

  • 2022 年 7 月 13 日
  • 本文字数:3089 字

    阅读完需:约 10 分钟

写在前面

每到周三公司就格外的忙,测试忙着提 bug,产品忙着改需求,UI 忙着发挥她的像素眼,尤其是下班前的一阵子,大家彷佛充满了干劲,颇有种再干他五百年的气势。就像一天的活都堆到傍晚,一周的活也都堆到周五,一言不合就加班。更惨的是加班回家还要哄女朋友睡觉,大冰块日更的大业差点被毁于一旦。


终于把女朋友哄睡着了,大冰块悄悄爬起床找个简单又冷门的题材再更它一篇。


很久很久之前,大冰块面试的时候,面试官问我 js 的数据类型,我想这还不简单?脱口而出 Number 、 String 、 Boolean 、 Object、 null 和 undefined ,还把它们的基本方法也说了一遍。面试官却意犹未尽的说:还有呢?ES6 新增的 Symbol 有用到吗?我说项目中暂时没有用到过,贵公司有用到吗?面试官迟疑了一会儿说没有。


我和面试官面面相觑,气氛一度有些尴尬。


所以今天就来说说这个 Symbol,避免下次面试的时候尴尬。

什么是 Symbol


Symbol 是 ES6 新增的一种基本数据类型。我们可以通过调用内置函数 Symbol() 创建,这个函数会动态的生成一个匿名、全局唯一的值。


举个例子吧:


Symbol 就好比我们的身份证号,没有谁的身份证号和别人是相同的,都是全国唯一的号码。


如:


let a = Symbol()let b = Symbol()a === b // false
复制代码


因为上述例子中,a 和 b 的值都是 Symbol(),虽然它们并不相等,但是我该如何区分它们呢?这里有个对 Symbol 不错的说明:


Symbol 函数栈不能用 new 命令,因为 Symbol 是原始数据类型,不是对象。可以接受一个字符串作为参数,为新创建的 Symbol 提供描述,用来显示在控制台或者作为字符串的时候使用,便于区分。


这时候就可以给Symbol()函数传参,参数可作为当前 Symbol 值的描述,即Symbol(描述内容)


读取描述内容可通过 Symbol 值转字符串的方法,也可通过 Symbol 值的属性description直接获取描述内容。示例如下:


const a = Symbol('描述啊');
String(a) // "Symbol(描述啊)"a.toString() // "Symbol(描述啊)"a.description // "描述啊"
// 注意:Symbol()返回的永远是全局唯一的值。// 所以Symbol() 和 Symbol() 并不相等。同样的,Symbol("描述")和Symbol("描述")也不相等哦。
复制代码

Symbol 有什么用

作为一名务实主义者,不知道它的用处就想让我去了解它,那是不可能的。


什么?你说不先了解怎么知道它的用处?这不就是“不工作怎么有经验?没经验怎么找工作?”的神奇悖论吗?


啊不扯了,我来告诉你 Symbol 最基本的两个用处


一,避免对象的键被覆盖。Symbol 用于对象的属性名时,能保证对象不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。

二,避免魔术字符串。魔术字符串的诠释是:在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。风格良好的代码,应该尽量消除魔术字符串,改由含义清晰的变量代替。


第一个用处容易理解,比如:


// 有一天大冰块声明了一个对象let obj = {    a:1,    b:2,    ...}
// 过了很久,同事小明用到了这个对象,不过因为对象里键值对繁多,并没有仔细筛查,小明给对象添加了新的键值对obj.a = 100 // 此时obj的值为obj = { a:100, ...}
// 如果其他地方在用obj.a的值,就会出现一系列的错误
// 所以小明给obj对象添加属性时,可以写成
const a = Symbol('a');obj[a] = 100;
// 此时obj的值为obj = { a: 1, b: 2, Symbol(a): 100}
复制代码


而第二个用处就不太好理解了,我们要先理解魔术字符串的含义。


以我最近在项目中看到的某位小伙伴代码为例:


const getUserInfo = state => {  if(state == 'yes'){       // 状态正确的后续操作    }else if(state == 'no'){        // 状态错误的后续操作             }else{         // 其他状态的后续操作  }}
// 在上述代码中,getUserInfo函数的传参"yes"和"no"都是魔术字符串,它们在多个接口中都被用到,不利于将来的修改和维护。// Symbol适用于原先写成魔术字符串的地方,因为它能保证自己是全局唯一的值。
// 所以代码可改写如下let status = { success: Symbol('success'), error: Symbol('error'), ...}const getUserInfo = state => { if(state === status.success){ // 状态正确的后续操作 }else if(state === status.error){ // 状态错误的后续操作 }else{ // 其他状态的后续操作 }}
getUserInfo(status.success)
复制代码


值得一提的是:


Symbol 作为对象属性名时读取时不能用.运算符,要用方括号。


以下面代码为例:


let state = Symbol()let obj = {}obj[state] = 500obj.state = 600obj // {state: 600, Symbol(): 500}
复制代码


这是因为.运算符后面是字符串,所以取到的是字符串 state 属性,而不是 Symbol 值 state 属性。


另外需要注意的是:


Symbol 值作为属性名时,该属性是公有属性不是私有属性,可以在类的外部访问。

但是在 for...in 、 for...of 的循环中不会出现该属性,同时 Object.keys() 、 Object.getOwnPropertyNames() 也不会 返回该属性。

如果要读取到一个对象中的 Symbol 属性,可以通过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到。


以下面代码为例:


const obj = {};const a = Symbol('a');obj[a] = 'a';for (let i in obj) {  console.log(i); // 无输出}Object.getOwnPropertyNames(obj) // []Object.getOwnPropertySymbols(obj) // [Symbol(a)]
复制代码

Symbol 的方法

Symbol.for()

既然 Symbol()生成的是一个全局唯一的值,那我们如果想重复使用某个 Symbol()生成的值怎么办呢?


这时候就需要用到Symbol.for()方法了。


Symbol.for()方法接受一个字符串作为参数,然后全局搜索有没有以该参数作为描述值的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局

悄悄告诉你:即使是在闭包函数中通过Symbol.for()方法生成的值,也会在全局中能被使用哦~


以下面代码为例:


let a = Symbol.for("唯一的")let b = Symbol.for("唯一的")a === b // true
复制代码


在上面的代码中,let a时全局检索有没有以"唯一的"作为描述值的 Symbol 值(必须是由 Symbol.for 方法创建的),发现没有就创建了一个Symbol.for("唯一的")let b时全局检索有没有以"唯一的"作为描述值的 Symbol 值,发现有,于是直接把找到的Symbol.for("唯一的")赋值给了 b,并没有重新去生成一个全局唯一的值。所以a === btrue

Symbol.keyFor()

如果我们要获取到全局注册的 Symbol 类型值的描述值该怎么做呢?


这时候就可以使用 Symbol.keyFor()方法了。


Symbol.keyFor()方法返回一个已全局注册的 Symbol 类型值的描述值,如果该 Symbol 类型值未全局注册,则返回undefined

它类似于通过 Symbol 值的属性description直接获取描述内容


以下面代码为例:


let a = Symbol.for("唯一的")let b = Symbol("唯一的")Symbol.keyFor(a) // 唯一的Symbol.keyFor(b) // undefined
复制代码


在上面的代码中,let aSymbol.for()方法生成了一个全局注册的描述值为"唯一的"的 Symbol 值,所以Symbol.keyFor(a)返回了描述值"唯一的"


let bSymbol()方法生成的值并不是全局注册的。所以Symbol.keyFor(b)直接返回了undefined

写在后面

看完了看懂了不代表自己真的掌握了,如果想要熟练的掌握并应用,还是离不开平时的使用和积累。所以希望看到这里的你下次就能在项目中用到 Symbol,有时候一段简单的代码,就能让老同事对你刮目相看,让新同事觉得你深不可测哟~


原创不易,如有错误,欢迎指正。

如果有帮助到你,请给大冰块悄悄点赞关注,你的点赞关注就是我写下去的动力。

让我们一起在前端的路上进步吧~🤭

发布于: 刚刚阅读数: 3
用户头像

还未添加个人签名 2022.07.01 加入

还未添加个人简介

评论

发布
暂无评论
《看完就懂系列》答应我,看完就开始用Symbol好吗?_7月月更_南极一块修炼千年的大冰块_InfoQ写作社区