《用得上的前端知识》系列 - 你我都很忙,能用100字说清楚,绝不写万字长文
简介
Symbol 是ES6中引入了一种新的基础数据类型,其特点有:
- 每个从Symbol() 返回的 symbol 值都是唯一的;
- symbol 值不可枚举,需使用特定的方法才能查看,如:
-
- Object.getOwnPropertySymbols
- Reflect.ownKeys
使用
let s1 = Symbol();
let s2 = Symbol();
let s3 = Symbol('name'); // 'name'只是一个描述信息,并不会影响 Symbol 的值
let s4 = Symbol('name');
console.log( s1 === s2 ); // false
console.log( s2 === s3 ); // false
console.log( s3 === s4 ); // false
属性遍历
let obj = {
[Symbol('name')]: '一斤代码',
age: 18,
title: 'Engineer'
}
// 使用Object的API
console.log( Object.getOwnPropertySymbols(obj) ); // [Symbol(name)]
// 使用新增的反射API
console.log( Reflect.ownKeys(obj) ); // [Symbol(name), "age", "title"]
应用场景
应用场景1:使用Symbol来作为对象属性名(key)
const PROP_NAME = Symbol()
const PROP_AGE = Symbol()
let obj = {
[PROP_NAME]: "一斤代码"
}
obj[PROP_AGE] = 18
obj[PROP_NAME] // '一斤代码'
obj[PROP_AGE] // 18
应用场景2:使用Symbol来替代常量
使用常量不好的地方在于,为常量赋一个唯一的值(比如这里的'AUDIO'、'VIDEO'、 'IMAGE'),常量少的时候还算好,但是常量一多,取名字就比较费神了,同时,常量一多,也容易出现重复。
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')
}
}
应用场景3:使用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('admin') // true
// 以下方式均无法访问 PASSWORD
login.PASSWORD;
login[PASSWORD];
login["PASSWORD"];
由于Symbol常量 PASSWORD 被定义在a.js所在的模块中,外面的模块获取不到这个Symbol,也不可能再创建一个一模一样的Symbol出来(因为Symbol是唯一的),因此这个 PASSWORD 的Symbol只能被限制在a.js内部使用,所以使用它来定义的类属性是没有办法被模块外访问到的,达到了一个私有化的效果。
注意事项
Symbol类型的key是不能通过 Object.keys() 或者 for...in 来枚举的,它未被包含在对象自身的属性名集合(property names)之中。所以,利用该特性,我们可以把一些不需要对外操作和访问的属性使用Symbol来定义。
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
适用于涉及到多个window(最典型的就是页面中使用了)的场景:
let gs1 = Symbol.for('global_symbol_1') //注册一个全局Symbol
let gs2 = Symbol.for('global_symbol_1') //获取全局Symbol
gs1 === gs2 // true