搞清楚ECMAScript 6新增的基本数据类型Symbol

前言

ECMAScript 6(简称ES6),于2015年6月正式发布的JavaScript语言的标准,正式名为ECMAScript 2015(ES2015)。ES6之前,基本数据类型分五种:Boolean、Number、String、Null 和 Undefined,ES6为防止因对象合并发生属性名冲突而被覆盖,增加了一种 Symbol 基本数据类型,表示独一无二的值

下面将从创建方式(Symbol() 函数 和 Symbol.for())、Symbol.for() 和 Symbol.keyFor()使用场景(作对象的属性名和替代某些场景下的常量)和 遍历(Object.getOwnPrototypeSymbols()和Reflect.ownKeys())四个方面来学习。

Symbol 介绍

创建 Symbol 类型值

创建 Symbol 类型值,不是通过 new 关键字(因为 new 关键字是创建一个对象的实例),而是 Symbol() 函数,其 typeof 值为 "symbol"

const nameProp = Symbol()

console.log(nameProp, typeof nameProp) // Symbol() "symbol"

因为 Symbol 表示的是独一无二的值,即使连着创建两个 Symbol 类型的值,也是不同的,例如:

const nameProp = Symbol()

const ageProp = Symbol()
console.log(nameProp, ageProp, nameProp === ageProp) // Symbol() Symbol() false

后面还会介绍另一种方式(Symbol.for())创建 Symbol 类型值。

增加描述内容

Symbol.prototype.description:访问其”实例“的描述

为字面化,易理解,建议增加描述内容即在 Symbol() 函数传入参数,通过 .description 访问描述
(该描述也会被视作该 Symbol 类型值的 key):

const nameProp = Symbol("name")


const ageProp = Symbol("age")





console.log(nameProp, nameProp.description) // Symbol(name) "local"

Symbol.for() 和 Symbol.keyFor()

如果要使用前面已创建过的 Symbol 类型值,可使用 Symbol.for(key),该方法接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局(Symbol.for() 定义的 Symbol 类型的值具有全局登记特性)。

Symbol.for()Symbol() 区别在于, 调用 Symbol("name") 30 次,返回 30 个不同的 Symbol 类型值,而 Symbol.for("name") 调用 30 次,返回的是 30 个相同的 Symbol 类型值。例如:

const ageProp = Symbol("age")
const childAgeProp = Symbol("age")
console.log(ageProp, childAgeProp, ageProp === childAgeProp) // Symbol(age) Symbol(age) false


const nameProp = Symbol.for("name")
const childNameProp = Symbol.for("name")
console.log(nameProp, childNameProp, nameProp === childNameProp) // Symbol(name) Symbol(name) true

Symbol.keyFor()方法接收一个 Symbol 类型的值,返回一个已登记的 Symbol 类型值的key,如果 Symbol 类型值是通过 Symbol() 创建的,因为没有被全局注册则返回 undefined;如果是通过 Symbol.keyFor() 创建的则返回其key(描述内容)。

console.log(Symbol.keyFor(ageProp)) // undefined
console.log(Symbol.keyFor(nameProp)) // name

Symbol 作用

因为 Symbol 类型为对象合并引起的属性名相同而被改写或覆盖,扩展对象的属性名类型,首先便是可做对象的属性名(ES6之前属性名只能是字符串类型,ES6后支持两种类型:字符串和Symbol类型)。又因为代表的是独一无二的值,可代替某些场景下的 const 定义的常量为什么说是某些场景?举例说明:定义常量 storageType 表示本地存储方式(localStorage OR sessionStorage),而后通过 storageType.getItem() 获取指定值, 这种场景下,便不可将 storagteType 定义为 Symbol 类型,因为 storageType 不仅是一个常量而且还可通过该常量去访问其值的属性或方法)。

作对象的属性名

const nameProp = Symbol("name")


const ageProp = Symbol("age")





const person = {
    [nameProp]: "露水晰123",
}
person[ageProp] = 18
console.log(person) // {Symbol(age): 18,Symbol(name): "露水晰123",__proto__: Object}

代替某些场景下的 const 常量

function getArea(shape, options) {
  let area = 0;



  switch (shape) {
    case 'Triangle': // 魔术字符串
      area = .5 * options.width * options.height;
      break;
    /* ... more code ... */
  }

  return area;
}

getArea('Triangle', { width: 100, height: 100 }); // 魔术字符串

消除魔术字符串常用的方式是将其改为一个变量,更改上述代码如下:

const shapeType = {

  triangle: 'Triangle'
};



function getArea(shape, options) {
  let area = 0;
  switch (shape) {
    case shapeType.triangle:
      area = .5 * options.width * options.height;
      break;
  }
  return area;
}

getArea(shapeType.triangle, { width: 100, height: 100 });

但是,仔细分析,可以发现shapeType.triangle等于哪个值并不重要,只要确保不会跟其他shapeType属性的值冲突即可。因此,这里就很适合改用 Symbol 值。上面代码中,除了将shapeType.triangle的值设为一个 Symbol,其他地方都不用修改。

const shapeType = {

  triangle: Symbol()
};

Symbol 属性名的遍历

Object.getOwnPropertySymbols() 只获取一级 Symbol 类型的属性名

Symbol 类型的属性名不会出现在 for...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。即以 Symbol 值作为键名,不会被常规方法遍历得到。我们可以利用这个特性,为对象定义一些非私有的、但又希望只用于内部的方法(外部不能访问)。

但是可以通过 Object.getOwnPropertySymbols(obj) 获取对象 obj 里定义的 Symbol 类型的属性名,以数组的形式返回。

const nameProp = Symbol("name")


const ageProp = Symbol("age")


const studentProp = Symbol("student")
const selfProp = Symbol("self")

const person = {
    date: new Date(),
    [nameProp]: "露水晰123",
}
person[ageProp] = 18
person[studentProp] = [
    { [nameProp]: "小晰1", [ageProp]: 19 },
    { [nameProp]: "小晰2", [ageProp]: 20 },
]
person[selfProp] = {
    [selfProp]: 18,
}
console.log(person)
console.log(Object.getOwnPropertySymbols(person))

打印结果如下:

image.png

Obejct.getOwnPropertySymbols(person) 返回的是对象 person 的一级属性名称中 Symbol 类型值,不包含深层的。

Reflect.ownKeys() 获取对象的所有一级属性

console.log(Reflect.ownKeys(person))

image.png

总结

  • Symbol 表示的是独一无二的值,通过 Symbol() 函数或者 Symbol.for() 函数创建,是基本数据类型;
  • Symbol 可以做为对象的属性名(ES6之前只能是字符串);
  • Symbol 可以代替某些场景下的 const 常量;
  • Symbol 不会出现在for...infor...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回,因为他们涉及的属性名是字符串;
  • 遍历对象的 Symbol 类型的属性,通过 Object.getOwnPropertySymbols(obj),但是只是一级的,不包含深层;Reflect.ownKeys() 可以获取对象的所有类型的键名(字符串和Symbol类型的);
  • Symbol.for() 全局登记特性,与 Symbol() 的区别在于调用多次前者返回的是相同的而后者返回的是不同的;
  • Symbol.keyFor() 返回一个已登记的 Symbol 类型值的 key,如果未登记则返回 undefined;

参考

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYv9bk4J' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片