Javascript Symbol原理及使用方法解析


Posted in Javascript onOctober 22, 2020

Symbol是ES6中新引入的一种基本数据类型,在此之前JavaScript中已有几种基本数据类型:

  • Numberg
  • String
  • Boolean
  • Null
  • Undefined
  • Object

不同于其他基本类型的通俗易懂,Symbol 是什么和有什么用一直有些让人困惑。

什么是Symbol

JavaScript标准中规定对象的key只能是 String 或 Symbol 类型,区别在于 String 类型的key可以重复而 Symbol 类型的key是唯一的。Symbol 的本质是表示一个唯一标识。每次创建一个Symbol,它所代表的值都不可能重复,该值的内部实现可以视为一段数字(类似:3423498431987719455..)。所以理论上 Symbol 的存在只有一个意义:用于必须使用唯一值的场景。

创建Symbol

创建 Number、String等基本类型的实例有两种方法:通过构造函数(或者叫工厂函数)和文字语法糖。比如:

// 构造函数
const num = Number(3);
const str = String('hi');

// 语法糖
const num = 3;
const str = 'hi';

显然使用语法糖更加简洁。但是 Symbol 只能通过构造函数 Symbol() 进行创建:

const sym = Symbol();

或者,我们可以传入一个字符串参数(descriptor)用于描述该Symbol:

const sym = Symbol('cat');

注意:传入的参数对 Symbol 值的产生并无影响,因为就算每次传入的参数都一样,生成的Symbol值也是不等的。该参数的作用仅用于描述被创建的Symbol,以便debug时可以识别出Symbol的含义。 所以,下列等式结果为 false:

Symbol('cat') === Symbol('cat') // false
Symbol.for(key)

和 Symbol() 类似,Symbol.for(key) 也可以创建一个Symbol,不一样的是:创建的 Symbol 是全局的(在全局Symbol表中注册),而如果全局已经存在相同 key 的Symbol,则直接返回该Symbol。所以,下列等式结果为 true:

Symbol.for('cat') === Symbol.for('cat') // true

如何使用Symbol

其实 Symbol 本身很简单,但是如何把它用好、且用的恰到好处却使人困惑,因为在平常工作中并没有多少非Symbol不用的场景。但是用对了Symbol会对你的代码质量有不少提升。来看下面几种案例:

1. 用作对象的key,防止命名冲突

使用Symbol作为Object的key,可以保证和其他key都不重复。因此,Symbol非常适合用于对对象的属性进行拓展。

比如,当使用 String 作为对象的key时,一旦出现重复的key则后面的属性会覆盖前面的:

const persons = {
 'bruce': 'wayne',
 'bruce': 'banner'
}

console.log(persons.bruce); // 'wayne'
使用Symbol作为Key可以避免这种情况:

const bruce1 = Symbol('bruce');
const bruce2 = Symbol('bruce');

const persons = {
 [bruce1]: 'wayne',
 [bruce2]: 'banner'
}

console.log(persons[bruce1]); // 'wayne'
console.log(persons[bruce2]); // 'banner'

js很多内建的方法都是通过 Symbol 进行指定的,比如:Symobol.iterator 指定了一个iterable对象的迭代器方法;Symbol.replace 指定了对象字符串替换的方法,这类 Symbol 被称为 Well-know Symbols,代表了js语言的内部行为。

2. 使用Symbol定义枚举

由于Javascript并不自带枚举类型,通常情况下我们会使用一个freezed的Object来模拟枚举类型,比如定义一个日期的枚举:

const DAYS = Object.freeze({
monday: 1,
tuesday: 2,
wednesday: 3
});

此时有一个方法,接收 DAYS 的枚举值来返回当天要做的事:

function getTodo(day) {
 switch (day) {
  case DAYS.monday:
   return "看电影";
  case DAYS.tuesday:
   return "购物";
  case DAYS.wednesday:
   return "健身";
  default:
   return "日期错误";
 }
}

我们希望代码逻辑足够严谨,传入的参数严格按照 DAYS.monday 的形式,否则就返回日期错误,但是该枚举类型的实现却做不到。比如:getTodo(1) 依然能得到 “看电影” 这个结果。

但是使用Symbol却可以解决这一问题,DAYS 枚举类型可以重新定义为:

const DAYS = Object.freeze({
monday: Symbol('monday'),
tuesday: Symbol('tuesday'),
wednesday: Symbol('wednesday')
});

此时 getTodo 方法必须接收 DAYS.monday 这样的枚举值作为参数,否则就返回 “日期错误”,因为世界上再没有任何一个值和 DAYS.monday 相等了。

这样定义枚举显然更严谨了。

3. 使用Symbol存储元数据

Key为Symbol类型的属性是不能被枚举的,这是 Symbol 除了唯一性外的第二大特性,因此使用for...in,Object.keys()、Object.hasOwnProperty()等方法不能识别Symbol属性,简而言之Symbol属性对用户是“隐藏”的(但并不是private的,因为有其他途径可以获取Symbol属性),例如:

Javascript Symbol原理及使用方法解析

因此Symbol作为“隐藏”属性可以用来存储对象的元数据。比如,有一个 TodoList:

class TodoList {
 constructor() {
  // todo数量
  this.count = 0;
 }

 // 增加todo
 add(id, content) {
  this[id] = content;
  this.count++;
 }
}

const list = new TodoList();

我们使用 add() 方法向其中增加几个todo:

list.add('a', '看电影');
list.add('b', '购物');
list.add('c', '健身');

当我们想使用 for...in 查看里面所有的todo时,会把 count 属性也带出来:

Javascript Symbol原理及使用方法解析

为了隐藏count属性,更方便的对todo进行操作,我们可以使用Symbol来存储它,TodoList 类修改为:

const count = Symbol('count');
class TodoList {
constructor() {
this[count] = 0;
}

add(id, content) {
this[id] = content;
this[count]++;
}
}

当我们再遍历 TodoList 的时候,count就隐藏了:

Javascript Symbol原理及使用方法解析

当我们想获取存储在Symbol中的原数据时,可以使用 Object.getOwnPropertySymbols() 方法:

Javascript Symbol原理及使用方法解析

以上是我能想到的 Symbol 的用途,如果大家有其他心得体会欢迎补充。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js 动态修改css文件用到了cssRule
Aug 20 Javascript
jQuery中toggleClass()方法用法实例
Jan 05 Javascript
解决Jquery向页面append新元素之后事件的绑定问题
Mar 16 Javascript
6种javascript显示当前系统时间代码
Dec 01 Javascript
jQuery打字效果实现方法(附demo源码下载)
Dec 18 Javascript
jQuery 常见小例汇总
Dec 14 Javascript
使用Javascript判断浏览器终端设备(PC、IOS(iphone)、Android)
Jan 04 Javascript
jquery实现图片平滑滚动详解
Mar 22 jQuery
详解vue-cli + webpack 多页面实例配置优化方法
Jul 13 Javascript
AngularJS service之select下拉菜单效果
Jul 28 Javascript
JS中比较两个Object数组是否相等方法实例
Nov 11 Javascript
jquery实现异步文件上传ajaxfileupload.js
Oct 23 jQuery
多个Vue项目部署到服务器的步骤记录
Oct 22 #Javascript
针对Vue路由history模式下Nginx后台配置操作
Oct 22 #Javascript
微信小程序基于高德地图API实现天气组件(动态效果)
Oct 22 #Javascript
ES11屡试不爽的新特性,你用上了几个
Oct 21 #Javascript
记一次vue跨域的解决
Oct 21 #Javascript
解决Vue项目中tff报错的问题
Oct 21 #Javascript
vue-cli3自动消除console.log()的调试信息方式
Oct 21 #Javascript
You might like
PHP CURL模拟登录新浪微博抓取页面内容 基于EaglePHP框架开发
2012/01/16 PHP
php中常用的预定义变量小结
2012/05/09 PHP
PHP基于SimpleXML生成和解析xml的方法示例
2017/07/17 PHP
Javascript 变量作用域 两个可能会被忽略的小特性
2010/03/23 Javascript
jquery获取input表单值的代码
2010/04/19 Javascript
JavaScript中的函数的两种定义方式和函数变量赋值
2014/05/12 Javascript
form.submit()不能提交表单的错误原因及解决方法
2014/10/13 Javascript
JavaScript中reduce()方法的使用详解
2015/06/09 Javascript
基于jQuery实现的双11天猫拆红包抽奖效果
2015/12/01 Javascript
JS实现图文并茂的tab选项卡效果示例【附demo源码下载】
2016/09/21 Javascript
Bootstrap源码解读按钮(5)
2016/12/23 Javascript
轻松学习Javascript闭包
2017/03/01 Javascript
原生Aajax 和jQuery Ajax 写法个人总结
2017/03/24 jQuery
关于Vue.nextTick()的正确使用方法浅析
2017/08/25 Javascript
微信小程序 input表单与redio及下拉列表的使用实例
2017/09/20 Javascript
解决Vue.js由于延时显示了{{message}}引用界面的问题
2018/08/25 Javascript
借助云开发实现小程序短信验证码的发送
2020/01/06 Javascript
解决vue字符串换行问题(绝对管用)
2020/08/06 Javascript
[03:44]2015国际邀请赛选手档案—Cloud9.NoTail
2015/07/28 DOTA
Python同时向控制台和文件输出日志logging的方法
2015/05/26 Python
python获得文件创建时间和修改时间的方法
2015/06/30 Python
Python多线程爬虫实战_爬取糗事百科段子的实例
2017/12/15 Python
python-itchat 统计微信群、好友数量,及原始消息数据的实例
2019/02/21 Python
在Django下创建项目以及设置settings.py教程
2019/12/03 Python
PyTorch的SoftMax交叉熵损失和梯度用法
2020/01/15 Python
opencv+python实现均值滤波
2020/02/19 Python
python 利用zmail库发送邮件
2020/09/11 Python
微软开源最强Python自动化神器Playwright(不用写一行代码)
2021/01/05 Python
网站开发实习生的自我评价
2013/12/11 职场文书
酒店节能降耗方案
2014/05/08 职场文书
党员目标管理责任书
2014/07/25 职场文书
小学生田径运动会广播稿
2014/09/11 职场文书
小学生2015教师节演讲稿
2015/03/19 职场文书
护士爱岗敬业心得体会
2016/01/25 职场文书
Python Pandas pandas.read_sql_query函数实例用法分析
2021/06/21 Python
mybatis 获取无数据的字段不显示的问题
2021/07/15 Java/Android