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 相关文章推荐
Asp.net下使用Jquery Ajax传送和接收DataTable的代码
Sep 12 Javascript
Javascript公共脚本库系列(一): 弹出层脚本
Feb 24 Javascript
html组件不可输入(只读)同时任何组件都有效
Apr 01 Javascript
模拟jQuery中的ready方法及实现按需加载css,js实例代码
Sep 27 Javascript
node.js中的fs.rmdir方法使用说明
Dec 16 Javascript
JavaScript 实现打印,打印预览,打印设置
Dec 30 Javascript
jQuery语法小结(超实用)
Dec 31 Javascript
url中的特殊符号有什么含义(推荐)
Jun 17 Javascript
浅谈Node.js:Buffer模块
Dec 05 Javascript
Vue.directive自定义指令的使用详解
Mar 10 Javascript
微信小程序 websocket 实现SpringMVC+Spring+Mybatis
Aug 04 Javascript
如何使用Jquery动态生成二级选项列表
Feb 06 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
乐信RP2100的电路分析和打磨
2021/03/02 无线电
php中数据的批量导入(csv文件)
2006/10/09 PHP
自动跳转中英文页面
2006/10/09 PHP
PHP正则表达式 /i, /is, /s, /isU等介绍
2014/10/23 PHP
解决更换PHP5.4以上版本后Dedecms后台登录空白问题的方法
2015/10/23 PHP
php 无限分类 树形数据格式化代码
2016/10/11 PHP
解决PHP使用CURL发送GET请求时传递参数的问题
2019/10/11 PHP
juqery 学习之三 选择器 可见性 元素属性
2010/11/25 Javascript
页面实时更新时间的JS实例代码
2013/12/18 Javascript
JS实现不规则TAB选项卡效果代码
2015/09/16 Javascript
基于jQuery实现select下拉选择可输入附源码下载
2016/02/03 Javascript
JS实现点击事件统计的简单实例
2016/07/10 Javascript
AngularJS基础 ng-cloak 指令简单示例
2016/08/01 Javascript
用JS动态设置CSS样式常见方法小结(推荐)
2016/11/10 Javascript
详解js中Number()、parseInt()和parseFloat()的区别
2016/12/20 Javascript
纯JS实现表单验证实例
2016/12/24 Javascript
jQuery使用正则表达式替换dom元素标签用法示例
2017/01/16 Javascript
Angular 4依赖注入学习教程之FactoryProvider配置依赖对象(五)
2017/06/04 Javascript
JavaScript事件处理程序详解
2017/09/19 Javascript
ElementUI Tag组件实现多标签生成的方法示例
2019/07/08 Javascript
Nodejs中使用puppeteer控制浏览器中视频播放功能
2019/08/26 NodeJs
详解vuejs中执行npm run dev出现页面cannot GET/问题
2020/04/26 Javascript
element-ui中dialog弹窗关闭按钮失效的解决
2020/09/22 Javascript
[54:26]完美世界DOTA2联赛PWL S3 Forest vs Rebirth 第一场 12.10
2020/12/12 DOTA
Python内置函数Type()函数一个有趣的用法
2015/02/18 Python
python实现寻找最长回文子序列的方法
2018/06/02 Python
python消除序列的重复值并保持顺序不变的实例
2018/11/08 Python
对python中Librosa的mfcc步骤详解
2019/01/09 Python
一篇文章弄懂Python中的可迭代对象、迭代器和生成器
2019/08/12 Python
python 用pandas实现数据透视表功能
2020/12/21 Python
HTML5 Plus 实现手机APP拍照或相册选择图片上传功能
2016/07/13 HTML / CSS
2015年大学班主任工作总结
2015/04/30 职场文书
暑期辅导班宣传单
2015/07/14 职场文书
外出培训学习心得体会
2016/01/18 职场文书
PHP遍历数组的6种方式总结
2021/11/17 PHP
10大幻兽系恶魔果实 蝙蝠果实上榜,第一自愈能力强
2022/03/18 日漫