欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

ES2015 Symbol数据类型

程序员文章站 2022-06-19 16:01:37
对象的属性名同名的问题在 ECMAScript2015 之前对象的属性名都是字符串类型,而字符串是有可能会重复的,那如果重复的话就会产生冲突。比如创建一个用来数据缓存的对象,约定这个对象是全局共享的,然后通过注释的方式模拟不同的 JavaScript 文件使用这个数据缓存对象的情况。比如在 shared.js 文件中创建这个数据缓存对象,如下代码所示:const cache = {}然后在 a.js 文件当中向数据缓存对象添加缓存数据,如下代码所示:cache['foo'] = Math.ran...

对象的属性名同名的问题

在 ECMAScript2015 之前对象的属性名都是字符串类型,而字符串是有可能会重复的,那如果重复的话就会产生冲突。比如创建一个用来数据缓存的对象,约定这个对象是全局共享的,然后通过注释的方式模拟不同的 JavaScript 文件使用这个数据缓存对象的情况。

比如在 shared.js 文件中创建这个数据缓存对象,如下代码所示:

const cache = {}

然后在 a.js 文件当中向数据缓存对象添加缓存数据,如下代码所示:

cache['foo'] = Math.random()

这里主要关注对象的属性名,也就是键的问题。然后在 b.js 文件当中再次向数据缓存对象添加缓存数据,如下代码所示:

cache['foo'] = '123'

这里很可能不知道这个缓存数据对象已经存在相同名称的键,也去使用相同名称作为键存放另外一个数据,这样就会产生冲突。

现如今会大量使用第三方模块,很多时候都会需要去扩展第三方模块中提供的一些对象,而此时是不知道这个对象当中是否会存在某一个指定的键,如果冒冒然地去扩展就有可能会产生冲突的问题。

以前解决这种问题的方式就是人为约定,比如这里约定在 a.js 文件放入的缓存键都以 a_ 开头,而 b.js 文件当中都以 b_ 开头。如下代码所示:

/* a.js */
cache['a_foo'] = Math.random()

/* b.js */
cache['b_foo'] = '123'

但是约定的方式只是规避了问题,并不是彻底地解决了这个问题。在这个过程当中如果有人不遵守这个约定的话,这个问题仍然存在。

Symbol 数据类型

ECMAScript2015 为了解决这样的问题新增了一个全新的数据类型叫 Symbol,译为符号,它的作用就是表示一个独一无二的值。

可以通过 Symbol() 函数创建一个 Symbol 类型的数据,如下代码所示:

const s = Symbol()
console.log(s)

上述代码的运行结果如下:

Symbol()

而且使用 typeof 运算符判断这个数据类型的结果就是 Symbol,这也就表示 Symbol 是一个全新的类型。如下代码所示:

console.log(typeof s)

Symbol 类型的最大特点就是独一无二,也就是说,通过 Symbol() 函数创建的每一个值都是唯一的。如下代码所示:

console.log(Symbol() === Symbol())

上述代码的运行结果如下:

false

考虑到在开发过程中的调试,Symbol() 函数允许传递一个字符串作为这个值的描述文本。这样的话,对于多次使用 Symbol 的情况就可以在控制台区分当前使用的到底是哪一个 Symbol 数据。如下代码所示:

console.log(Symbol('foo'))
console.log(Symbol('bar'))
console.log(Symbol('baz'))

上述代码的运行结果如下:

Symbol(foo)
Symbol(bar)
Symbol(baz)

而且从 ECMAScript2015 开始,对象就可以直接使用 Symbol 类型的值作为属性名。也就是说,现在对象的属性名可以是两种类型,分别是 StringSymbol。如下代码所示:

const obj = {}
obj[Symbol()] = '123'
obj[Symbol()] = '456'

console.log(obj)

上述代码的运行结果如下:

{ [Symbol()]: '123', [Symbol()]: '456' }

从打印的结果可以看到,因为 Symbol 值都是独一无二的,所以这里就不用担心可能会产生冲突的问题了。这里也可以使用计算属性名的方式直接在对象字面量当中使用 Symbol 作为属性名。如下代码所示:

const obj = {
  [Symbol()]: 123
}

模拟实现对象的私有成员

另外,Symbol 除了可以避免对象的属性名重复产生的问题,还可以借助这种类型的特点模拟实现对象的私有成员。如下代码所示:

/* a.js */
const name = Symbol()
const person = {
  [name]: '前端课湛',
  say() {
    console.log(this[name])
  }
}

/* b.js */
console.log(person[Symbol()])
person.say()

如果在 a.js 文件中创建一个对象,使用 Symbol 作为其中一个属性名的话,那在外部(b.js 文件)是无法访问到以 Symbol 作为属性名的属性或方法的。

上述代码的运行结果如下:

undefined
前端课湛

Symbol 类型最主要的作用就是为对象添加独一无二的属性名。

Symbol 的唯一性

Symbol 在使用上还有一些值得注意的地方。首先是 Symbol 的唯一性,每次通过 Symbol 创建的值一定是唯一的值,不管传入的文本描述是不是相同的。如下代码所示:

console.log(
  Symbol() === Symbol(),
  Symbol('foo') === Symbol('foo')
)

上述代码的运行结果如下:

false false

如果需要在全局复用一个相同的 Symbol 值,可以使用全局变量的方式去实现,或者使用 Symbol 提供的一个 for() 静态方法去实现。如下代码所示:

const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
console.log(s1 === s2)

上述代码的运行结果如下:

true

for() 方法内部维护了一个全局的注册表,为字符串和 Symbol 值提供了一个一一对应的关系。需要注意的是for() 方法内部维护的是字符串和 Symbol 之间的关系。也就是说,如果传入的不是字符串,for() 方法内部会自动把它转换成字符串。如下代码所示:

console.log(Symbol.for(true) === Symbol.for('true'))

上述代码的运行结果如下:

true

从上述打印结果可以看到,for() 方法内部将 Boolean 类型的 true 转换成了 String 类型的 true,所以两者比较的结果为 true。

Symbol 的这一特点尤其需要注意。

Symbol 提供的常量

而且,在 Symbol 类型还提供了很多内置的 Symbol 常量,用来作为内部方法的标识。这些标识符可以让自定义对象去实现一些 JavaScript 当中内置的接口。如下代码所示:

const obj = {}
console.log(obj.toString())

上述代码的运行结果如下:

[object Object]

上述打印结果被称为 obj 对象的 toString 标签。如果想要自定义这个对象的 toString 标签,就可以在这个对象当中添加一个特殊的成员来进行标识。如下代码所示:

const obj = {
  [Symbol.toStringTag]: 'XObject'
}
console.log(obj.toString())

上述代码的运行结果如下:

[object XObject]

Symbol 值作为对象属性名的特性

最后,使用 Symbol 值作为对象的属性名,通过 for...in 循环是无法获取的。如下代码所示:

const obj = {
  [Symbol()]: 'Symbol value',
  foo: 'normal value'
}

for (let key in obj) {
  console.log(key)
}

上述代码的运行结果如下:

foo

而且,通过 Object.keys() 方法同样无法获取使用 Symbol 值作为对象的属性名。再有就是如果通过 JSON.stringify() 序列化对象为一个 JSON 字符串的话,使用 Symbol 值作为对象的属性名也会被忽略掉。

总之,这些特性都使得 Symbol 类型的属性特别适合作为对象的私有属性。当然,想要获取这种类型的属性名也不是没有办法的,可以使用 Object.getOwnPropertySymbols() 方法。如下代码所示:

console.log(Object.getOwnPropertySymbols(obj))

上述代码的运行结果如下:

[ Symbol() ]

Object.getOwnPropertySymbols() 方法的作用类似于 Object.keys() 方法,所不同的是 Object.keys() 方法只能获取字符串作为属性名的属性,而 Object.getOwnPropertySymbols() 方法获取的都是 Symbol 作为属性名的属性。

本文地址:https://blog.csdn.net/kingj_fullstack/article/details/107544042