当前位置: 移动技术网 > IT编程>开发语言>JavaScript > ES2015 Symbol数据类型

ES2015 Symbol数据类型

2020年07月24日  | 移动技术网IT编程  | 我要评论

对象的属性名同名的问题

在 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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网