Symbol 的概念

symbol 是一种基本数据类型 (primitive data type)。Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。

1  每个从Symbol()返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符;这是该数据类型仅有的目的

也就是说每一个Symbol的值都是不同的

const symbol1 = Symbol();
const symbol2 = Symbol('foo');
const symbol3 = Symbol('foo');console.log(typeof symbol1);
// expected output: "symbol"console.log(symbol2 === 42);
// expected output: falseconsole.log(symbol3.toString());
// expected output: "Symbol(foo)"console.log(Symbol('foo') === Symbol('foo'));
// expected output: falselet copyValue1 = 2
let copyValue2 = 2
console.log(copyValue1,copyValue2,Symbol('foo'),Symbol('foo'),copyValue1==copyValue2,symbol2==symbol3)
//2 2 Symbol(foo) Symbol(foo) true false

上面的代码创建了三个新的symbol类型。 注意,Symbol("foo") 不会强制将字符串 “foo” 转换成symbol类型。它每次都会创建一个新的 symbol类型:

全局共享的 Symbol

上面使用Symbol() 函数的语法,不会在你的整个代码库中创建一个可用的全局的symbol类型。 要创建跨文件可用的symbol,甚至跨域(每个都有它自己的全局作用域) , 使用 Symbol.for() 方法和  Symbol.keyFor() 方法从全局的symbol注册表设置和取得symbol。

在对象中查找 Symbol 属性

Object.getOwnPropertySymbols() 方法让你在查找一个给定对象的符号属性时返回一个symbol类型的数组。注意,每个初始化的对象都是没有自己的symbol属性的,因此这个数组可能为空,除非你已经在对象上设置了symbol属性。

Symbol 包装器对象作为属性的键

当一个 Symbol 包装器对象作为一个属性的键时,这个对象将被强制转换为它包装过的 symbol 值:

var sym = Symbol("foo");
var obj = {[sym]: 1};
obj[sym];            // 1
obj[Object(sym)];    // still 1

对 symbol 使用 typeof 运算符

typeof运算符能帮助你识别 symbol 类型

typeof Symbol() === 'symbol'
typeof Symbol('foo') === 'symbol'
typeof Symbol.iterator === 'symbol'

Symbol 类型转换

当使用 symbol 值进行类型转换时需要注意一些事情:

  • 尝试将一个 symbol 值转换为一个 number 值时,会抛出一个 TypeError 错误  (e.g. +sym or sym | 0).
  • 使用宽松相等时, Object(sym) == sym returns true.
  • 这会阻止你从一个 symbol 值隐式地创建一个新的 string 类型的属性名。例如,Symbol("foo") + "bar" 将抛出一个 TypeError (can't convert symbol to string).
  • "safer" String(sym) conversion 的作用会像symbol类型调用 Symbol.prototype.toString() 一样,但是注意 new String(sym) 将抛出异常。

Symbols 与 for...in 迭代

Symbols 在 for...in 迭代中不可枚举。另外,Object.getOwnPropertyNames() 不会返回 symbol 对象的属性,但是你能使用 Object.getOwnPropertySymbols() 得到它们。

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); // logs "c" and "d"
}

Symbols 与 JSON.stringify()

当使用 JSON.stringify() 时,以 symbol 值作为键的属性会被完全忽略:

JSON.stringify({[Symbol("foo")]: "foo"});
// '{}'

更多细节,请看 JSON.stringify()

应用场景1:使用Symbol来作为对象属性名(key)

在这之前,我们通常定义或访问对象的属性时都是使用字符串,比如下面的代码:

let obj = {abc: 123,"hello": "world"
}obj["abc"] // 123
obj["hello"] // 'world'

而现在,Symbol可同样用于对象属性的定义和访问:

 
const PROP_NAME = Symbol()
const PROP_AGE = Symbol()let obj = {[PROP_NAME]: "一斤代码"
}
obj[PROP_AGE] = 18obj[PROP_NAME] // '一斤代码'
obj[PROP_AGE] // 18

随之而来的是另一个非常值得注意的问题:就是当使用了Symbol作为对象的属性key后,在对该对象进行key的枚举时,会有什么不同?在实际应用中,我们经常会需要使用Object.keys()或者for...in来枚举对象的属性名,那在这方面,Symbol类型的key表现的会有什么不同之处呢?来看以下示例代码:

 
let obj = {[Symbol('name')]: '一斤代码',age: 18,title: 'Engineer'
}Object.keys(obj)   // ['age', 'title']for (let p in obj) {console.log(p)   // 分别会输出:'age' 和 'title'
}Object.getOwnPropertyNames(obj)   // ['age', 'title']

由上代码可知,Symbol类型的key是不能通过Object.keys()或者for...in来枚举的,它未被包含在对象自身的属性名集合(property names)之中。所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。

也正因为这样一个特性,当使用JSON.stringify()将对象转换成JSON字符串的时候,Symbol属性也会被排除在输出内容之外:

JSON.stringify(obj)  // {"age":18,"title":"Engineer"}

我们可以利用这一特点来更好的设计我们的数据对象,让“对内操作”和“对外选择性输出”变得更加优雅。

然而,这样的话,我们就没办法获取以Symbol方式定义的对象属性了么?非也。还是会有一些专门针对Symbol的API,比如:

 
// 使用Object的API
Object.getOwnPropertySymbols(obj) // [Symbol(name)]// 使用新增的反射API
Reflect.ownKeys(obj) // [Symbol(name), 'age', 'title']

应用场景2:使用Symbol来替代常量

先来看一下下面的代码,是不是在你的代码里经常会出现?

 
const TYPE_AUDIO = 'AUDIO'
const TYPE_VIDEO = 'VIDEO'
const TYPE_IMAGE = 'IMAGE'function handleFileResource(resource) {switch(resource.type) {case TYPE_AUDIO:playAudio(resource)breakcase TYPE_VIDEO:playVideo(resource)breakcase TYPE_IMAGE:previewImage(resource)breakdefault:throw new Error('Unknown type of resource')}
}

如上面的代码中那样,我们经常定义一组常量来代表一种业务逻辑下的几个不同类型,我们通常希望这几个常量之间是唯一的关系,为了保证这一点,我们需要为常量赋一个唯一的值(比如这里的'AUDIO'、'VIDEO'、 'IMAGE'),常量少的时候还算好,但是常量一多,你可能还得花点脑子好好为他们取个好点的名字。

现在有了Symbol,我们大可不必这么麻烦了:

 
  1. const TYPE_AUDIO = Symbol()

  2. const TYPE_VIDEO = Symbol()

  3. const TYPE_IMAGE = Symbol()

这样定义,直接就保证了三个常量的值是唯一的了!是不是挺方便的呢。

应用场景3:使用Symbol定义类的私有属性/方法

我们知道在JavaScript中,是没有如Java等面向对象语言的访问控制关键字private的,类上所有定义的属性或方法都是可公开访问的。因此这对我们进行API的设计时造成了一些困扰。

而有了Symbol以及模块化机制,类的私有属性和方法才变成可能。例如:

  • 在文件 a.js中
const PASSWORD = Symbol()class Login {constructor(username, password) {this.username = usernamethis[PASSWORD] = password}checkPassword(pwd) {return this[PASSWORD] === pwd}
}export default Login
  • 在文件 b.js 中
 
import Login from './a'const login = new Login('admin', '123456')login.checkPassword('admin')  // truelogin.PASSWORD  // oh!no!
login[PASSWORD] // oh!no!
login["PASSWORD"] // oh!no!

由于Symbol常量PASSWORD被定义在a.js所在的模块中,外面的模块获取不到这个Symbol,也不可能再创建一个一模一样的Symbol出来(因为Symbol是唯一的),因此这个PASSWORD的Symbol只能被限制在a.js内部使用,所以使用它来定义的类属性是没有办法被模块外访问到的,达到了一个私有化的效果。

参考文档

【1】https://blog.csdn.net/weixin_33711641/article/details/89659385?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7Edefault-5.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EOPENSEARCH%7Edefault-5.control

【2】https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol#规范

Symbol 类型 的简单理解和应用相关推荐

  1. JavaScript 为什么要有 Symbol 类型?

    摘要: 为什么比怎么用更有意义. 原文:JavaScript 为什么要有 Symbol 类型 作者:前端小智 Symbols 是 ES6 引入了一个新的数据类型 ,它为 JS 带来了一些好处,尤其是对 ...

  2. (cljs/run-at (JSVM. :browser) 简单类型可不简单啊~)

    前言  每逢学习一个新的语言时总要先了解这门语言支持的数据类型,因为数据类型决定这门语言所针对的问题域,像Bash那样内置只支持字符串的脚步明显就是用于文本处理啦.而数据类型又分为标量类型(Scala ...

  3. 一文简单理解《Effective Java》建议

    考虑用静态工厂方法替代构造方法 传统的获取一个对象实例,通常是通过构造方法,new一个对象:不同数量的入参,会有不同的构造方法: 例如,统一的返回结果类,传统方式(伪代码)如下: //成功 retur ...

  4. [JavaScript] 好用的 JavaScript Symbol 类型

    初识Symbol 什么是Symbol? 可以理解它为一个标识,一般情况下用来解决重名问题. let hd = Symbol('hello,dust.') let edu = Symbol('good ...

  5. ES6笔记(4)-- Symbol类型

    系列文章 -- ES6笔记系列 Symbol是什么?中文意思是标志.记号,顾名思义,它可以用了做记号. 是的,它是一种标记的方法,被ES6引入作为一种新的数据类型,表示独一无二的值. 由此,JS的数据 ...

  6. linun——SElinux的简单理解

    SElinux简单理解 一.什么是SElinux? SELinux 全称 Security Enhanced Linux (安全强化 Linux),是 MAC (Mandatory Access Co ...

  7. c语言理解参数,c语言中对可变参数列表的简单理解

    函数原型中一般情况下参数的数目是固定的,但是如果想在不同的时候接收不定数目的参数时该怎么办呢?c语言提供了可变参数列表来实现. 可变参数列表是通过宏来实现的,这些宏定义在stdarg.h的头文件中.头 ...

  8. 入门启发:音视频的简单理解

    算机技术领域中,『音视频技术』应该说算是较复杂的小门类.较复杂的东西有个简单的入门指引,或者有前辈带路是很重要的. 前阵子,因为项目中急需音视频技术,虽然网上资料看似很丰富,但对初学者来说,很多资料都 ...

  9. 浏览器解析jsx_简单理解JavaScript,TypeScript和JSX

    原标题:简单理解JavaScript,TypeScript和JSX Java: 基本概念: Java一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为Java引 ...

  10. Javascript闭包简单理解

    Javascript闭包简单理解 原文:Javascript闭包简单理解 提到闭包,想必大家都早有耳闻,下面说下我的简单理解. 说实话平时工作中实际手动写闭包的场景并不多,但是项目中用到的第三方框架和 ...

最新文章

  1. 盘点提高国内访问 GitHub 的速度的 9 种方案
  2. 这篇文章很好的诠释了为什么安全框架如此重要?
  3. Java高并发编程(十二):Executor框架
  4. C#中的数据类型转换
  5. 实时通信服务中的语音解混响算法实践
  6. Lucene.Net学习
  7. php无法下单功能,PHP如何解决并发下单问题?(不一定是下单,举个例)
  8. 【S4 导数据】S/4HANA导数,只会LSMW远远不够(转)
  9. 调用postman拿CSRF token的隐藏Cookie处理
  10. 创建外部快照_快照事件:现在如何仅通过拍照即可创建日历事件
  11. linux中的运行模式,Linux系统运行模式介绍
  12. break 和 continue 语句, 以及循环中的 else 子句
  13. 【华为云技术分享】从部署和运维说说DLI(1)
  14. android layout引入活动,Android 开发入门-活动的基本用法
  15. 总结:ADO.NET在开发中的部分使用方法和技巧
  16. 130242014075 杨利城 《电商系统某功能模块》的需求分析与设计
  17. 【web前端期末大作业】html在线网上书店 基于html制作我的书屋(23页面)
  18. 可视化图表告诉你,《人世间》《余生,请多指教》到底爆没爆?
  19. mysql的联接算法_【MySQL—SQL编程】联接
  20. TopK Question

热门文章

  1. ALOS卫星轨道插值并绘制轨道
  2. 疫情期间,找工作的一些建议
  3. 如何把视频生成二维码,手机扫一扫就可以看
  4. 小黄鸡 php,Simsimi (小黄鸡) API接口(PHP)公布,小黄鸡API接口非官方PHP版本来啦...
  5. 教师不能错过的三款教学必备工具
  6. A. Captain Flint and Crew Recruitment
  7. ATOM Z3000 家族整理之基本信息(一) V2.0版
  8. opc ua与opc da区别_OPC,OPCDA,OPCUA
  9. Egret MovieClip2
  10. Oracle中的sys用户和system用户