理解和使用ES6中的 Symbol
原文:https://www.jianshu.com/p/f40a77bbd74e
ES6中引入了一种新的基础数据类型symbol
,但是一直以来都没什么机会了解,今天看了一篇文章里面偶然提到,然后发现自己不懂,于是就去百度了一波。然后就发现了这么一篇精品。为了加深自己的理解,于是转载过来。同时也方便自己以后复习用。
如果转载的不好,还请移步到原文。
这是一种新的基础数据类型 (primitive type)
symbol
是由ES6 规范引入的一项新特性,它的功能类似于一种唯一标识的id。我们一般直接调用symbol()
来创建一个symbol
实例。
let s1 = Symbol()
或者你也可以在调用Symbol()
的时候,传入一端字符串。该字符串相当于Symbol实例的描述
let s2 = Symbol('another symbol')
由于symbol
是一种基础的数据类型,所以当我们用typeof
查看的时候 返回的是 symbol
typeof s1 // 'symbol'
同时symbol 的实例时唯一的,所以,当你比较两个symbol的时候,总会返回false
let s1 = Symbol()
let s2 = Symbol('another symbol')
let s3 = Symbol('another symbol')
s1 === s2 // false
s2 === s3 // false
应用场景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]:"huong"
}
obj[PROP_AGE] = 18
obj[PROP_NAME] // "huong"
obj[PROP_AGE] // 18
关于对象的枚举。如果使用了Symbol定义的属性,在使用传统的for..in
或Object.keys()
是不能枚举到的。
let obj = {
[Symbol('name')]:"huong",
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是不能通过传统方式去枚举出来的。因为它并未被包含在自身的属性名合集中(property names)。所以利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。
同样,使用JSON.stringify()
将对象转成字符串时候,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', 'tile' ]
应用场景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)
break
case TYPE_VIDEO:
playVideo(resource)
break
case TYPE_IMAGE:
previewImage(resource)
break
default:
throw new Error('Unknown type of resource')
}
}
现在有了Symbol
我们可以改成这样
const TYPE_AUDIO = Symbol()
const TYPE_VIDEO = Symbol()
const TYPE_IMAGE = Symbol()
function handleFileResource(resource) {
switch (resource) {
case TYPE_AUDIO:
console.log('TYPE_AUDIO')
break
case TYPE_VIDEO:
console.log('TYPE_VIDEO')
break
case TYPE_IMAGE:
console.log('TYPE_IMAGE')
break
default:
throw new Error('Unknown type of resource')
}
}
// 当你想要判断的时候只需要把值传进去
handleFileResource(TYPE_VIDEO) // TYPE_AUDIO
应用场景3 使用Symbol定义类的私有属性/方法
使用Symbol
可以让模块里面的属性和方法变成私有
- 文件 a.js
const PASSWORD = Symbol()
class Login {
constructor(username, password) {
this.username = username,
this[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('123456') // true
login.PASSWORD // oh!no!
login[PASSWORD] // oh!no!
login['PASSWORD'] // oh!no!
由于Symbol常量被定义在a.js中,外面的模块是访问不了这个Symbol的。因此可以达成一个私有变量。
注册和获取全局Symbol
通常情况下,我们在一个浏览器窗口中(window),使用Symbol()
函数来定义和Symbol实例就足够了。但是如果你想你想多个window中使用某些Symbol是同一个。那就不能使用Symbol()
函数。需要用到另一个API去定义Symbol.for()
它可以注册或获取一个window间全局的Symbol实例
let gs1 = Symbol.for('golbal_symbol_1') // 注册一个全局Symbol
let gs2 = Symbol.for('global_symbol_1') // 获取全局的Symbol
gs1 === gs2 // true
这样一个Symbol不光在单个window中是唯一的,在多个相关window间也是唯一的了。
以上
网友评论