JavaScript —— Symbol数据类型之不延伸
导语:
Symbol
是一种基本数据类型,每个从Symbol()
返回的值都是唯一的。一个symbol
的值能作为对象属性的标识符,这是该数据类型仅有的目的。 ---- 《摘自MDN》
敲黑板:唯一的、标识符
这句话值得我们细细咀嚼,我们由上面这句话来展开思考。说到唯一性,我们可以联想到的数据类型有什么?
下面我们就这三种数据类型展开对比:
一、叭叭JavaScript的Symbol
属于是ES6
新增的数据类型,在ES6
之前,只有string
,number
,boolean
,null
,undefined
和Object
六种数据类型,Symbol
属于新增的基本数据类型。后面还新增了BigInt
数据类型等。
- 创建
Symbol
,简单举个例子:
let a = Symbol('a'); // 也可以直接 Symbol()
这样就创建好了一个Symbol
数据类型,任何其他的数据类型都与之不相等,例如:
let b = Symbol('a');
console.log(a == b) // false
我们在使用console.log()
去输出Symbol
的时候,会隐式调用Symbol
的toString()
方法,例如:
console.log(a); // Symbol(a)
反之,我们使用typeof
去控台输出,返回的是数据类型,例如:
console.log(typeof a); // symbol
- 创建
Symbol
的时候,可以添加一个可选参数作为描述,这样使Symbol更具有语义性,例如:
let name = Symbol('name');
console.log(name); // Symbol(name)
- 获取
Symbol
的标识符,使用Symbol.keyFor()
方法,例如:
let c = Symbol('d');
console.log(Symbol.keyFor(c)); // 输出 d
备注:如果不添加标识符参数创建变量之后,该方法输出为undefined
,例如:
let e = Symbol();
console.log(Symbol.keyFor(e)); // 输出 undefined
Symbol
的共享,虽然不知道有啥用,例如:
let a = Symbol.for('a');
let b = Symbol.for('a');
console.log(a == b); // true
二、TypeScript的Symbol
跟JavaScript的一样,因为TypeScript
只是一个JavaScript超级,本质还是会将TS
代码通过TSLint
(虽然已经废用),转换成JavaScript代码,你可以理解为同Babel
差不多,因此不再赘述。
三、Symbol的作用及延伸
- 唯一的值,例如:
let orderStatus = {
OPEN: Symbol('已下单'),
ON: Symbol('配送中'),
COMPLETED: Symbol('订单完成'),
CANCELED: Symbol('取消订单')
}
看起来似乎很美好对吧?每个状态都有自己唯一的值,类似于enum
。只是,,
通常,我们都晓得,获取到的后端数据,用于判断状态和入库的数据一般都是0,1,2…
为什么?
- 第一,从字节编码层面考虑,可以避免编码格式问题,你的’男’或者’女’如果编码格式有误则乱码入库,后患无穷;
- 第二,从存储空间层面考虑,UTF8的一个汉字占三个字节,而一个数字只有一个字节…且更加方便开发者维护;
- 第三,从数据库检索层面考虑,字节越少,耗时约短,查询的效率随之更高。
这时候我问你,你难道还希望后端返回Symbol
数据类型给你吗?
那这时候有朋友就说:啊我不管,我就要这种数据类型,看起来多酷哇!
如果是,那我希望你不会被施与爱的关怀…万一咱们后端同事同意使用这种数据类型了,那我希望你们以后不会被维护的人隔空给你们来一句爱的问候~(dog狗头)
- 唯一的
key
,例如:
let status = Symbol('status');
let ordering = {
[status]: 1,
description: '杨国福麻辣烫加麻加辣!'
};
console.log(ordering); // {description: '杨国福麻辣烫加麻加辣!', Symbol(status): 1}
使用Object.keys()
获取对象的所有可枚举属性,例如:
console.log(Object.keys(ordering)); // ["description"]
使用Object.getOwnPropertyNames()
获取所有属性,无论是否可以枚举,例如:
console.log(Object.getOwnPropertyNames(ordering)); // ["description"]
那么要获取对象中的Symbol
属性,需要使用ES6
新增的Object.getOwnPropertySymbols()
方法,例如:
console.log(Object.getOwnPropertySymbols(ordering)); // [Symbol(ordering)]
- 众所周知的Symbols
除了用户定义的Symbols
,还有一些已经众所周知的内置Symbols
。 内置Symbols
用来表示语言内部的行为。
Symbol.hasInstance
方法,会被instanceof
运算符调用。构造器对象用来识别一个对象是否是其实例。Symbol.isConcatSpreadable
布尔值,表示当在一个对象上调用Array.prototype.concat
时,这个对象的数组元素是否可展开。Symbol.iterator
方法,被for-of
语句调用。返回对象的默认迭代器。Symbol.match
方法,被String.prototype.match
调用。正则表达式用来匹配字符串。Symbol.replace
方法,被String.prototype.replace
调用。正则表达式用来替换字符串中匹配的子串。Symbol.search
方法,被String.prototype.search
调用。正则表达式返回被匹配部分在字符串中的索引。Symbol.species
函数值,为一个构造函数。用来创建派生对象。Symbol.split
方法,被String.prototype.split
调用。正则表达式来用分割字符串。Symbol.toPrimitive
方法,被ToPrimitive
抽象操作调用。把对象转换为相应的原始值。Symbol.toStringTag
方法,被内置方法Object.prototype.toString
调用。返回创建对象时默认的字符串描述。Symbol.unscopables
对象,它自己拥有的属性会被with作用域排除在外。
四、总结
通过开头的导语、通篇的分析对比特性,我们知道:Symbol
它的目的仅仅是为对象属性添加独特一个标识符而已。而我们之前分析的const
常量和TypeScript enum
枚举,它俩都有添加标识符的能力,但总的就一句话,哪种数据类型更加契合业务环境,你就用哪种。如果在复杂性较高的大型项目里,针对对象属性,想要规避出现相同的数据造成的变量混乱,Symbol
永远是你的最优选择;想要区分不同业务场景下使用的某些变量,从而达到规避魔数、整洁代码的目的,enum
是不二之选;相反,const
常量往往都是作为一个固定的数据出现,例如公数、当前日期时间等等…