今天我们来学习JavaScript新的原始类型Symbol
,我们可以使用Symbol来创建唯一值作为对象属性或者值,也可以通过Symbol
的well-known
来修改JS语言内部的逻辑。
创建Symbol
ES6新增了Symbol
作为原始数据类型,和其他原始数据类型,像number、boolean、null、undefined、string,symbol类型没有文字形式。
创建一个symbol,我们要使用全局函数Symbol()
lets=Symbol('foo');
Symbol()
函数每次调用会创建一个新的唯一值
console.log(Symbol()===Symbol());//false
Symbol()
函数接受一个可选参数作为描述,这样使Symbol更具有语义性。
下面创建两个symbol分别为: firstName
and lastName
.
letfirstName=Symbol('firstname'),lastName=Symbol('lastname');
当我们使用console.log()去打印symbol的时候会隐式调用symbol的toString()方法。
console.log(firstName);//Symbol(firstname)console.log(lastName);//Symbol(lastname)
由于symbol为原始值,我们可以使用typeof
去检查它的类型,同样ES6拓展了typeof关键字,在遇到symbol类型时会返回symbol
console.log(typeoffirstName);//symbol
由于是原始类型,也不能使用new去创建
lets=newSymbol();//error
共享 symbol
要创建一个共享的symbol,要使用Symbol.for()
函数,而不是Symbol()
。
Symbol.for()
也接受一个可选参数作为描述
letssn=Symbol.for('ssn');
Symbol.for()
会首先在全局中查找是否有已经创建的ssn
的symbol,如果有就会返回已经创建的symbol,如果没有就会创建一个新的symbol。
接下来,我们创建一个相同的symbol,然后看看不是同一个symbol
letnz=Symbol.for('ssn');console.log(ssn===nz);//true
因为上面已经创建ssn
的symbol,所以nz变量的symbol和上面创建的将是同一个。
如果想要获取symbol的键,使用Symbol.keyFor()方法
console.log(Symbol.keyFor(nz));//'ssn'
注意,如果symbol是通过Symbol()
创建的,使用Symbol.keyFor()
会返回undefined
letsystemID=Symbol('sys');console.log(Symbol.keyFor(systemID));//undefined
Symbol 有啥用
一) 使用Symbol作唯一值
我们在代码中经常会用字符串或者数字去表示一些状态,也经常会面临缺乏语义性或者重复定义的问题,这时使用Symbol是最好的选择,每次新创建的Symbol都是唯一的,不会产生重复,而且我们可以给Symbol传入相应的描述。
看下面的例子,我们使用Symbol来表达订单的几种状态,而不是字符串和数字
console.log(Symbol()===Symbol());//false0
二) 使用 symbol 作为对象属性
使用Symbol
作为属性名称
console.log(Symbol()===Symbol());//false1
使用Object.keys()
获取对象的所有可枚举属性
console.log(Symbol()===Symbol());//false2
使用Object.getOwnPropertyNames()
获取所有属性,无论是否是可枚举
console.log(Symbol()===Symbol());//false3
那么要获取对象中的Symbol属性,需要使用ES6新增的Object.getOwnPropertySymbols()
方法
console.log(Symbol()===Symbol());//false4
Well-known symbol
ES6在原型链上定义了与 Symbol
相关的属性来暴露更多的语言内部逻辑。well-known Symbol
为标准对象定义了一些以前只在语言内部可见的功能。
Symbol.hasInstance
Symbol.hasInstance
是一个改变instanceof
操作符默认行为的symbol,通常我们会这样使用instanceof
console.log(Symbol()===Symbol());//false5
那么JavaScript 就会执行 Symbol.hasIntance
方法,像下面这样
console.log(Symbol()===Symbol());//false6
它会调用type的Symbol.hasInstance
静态方法,将obj作为参数
console.log(Symbol()===Symbol());//false7
[]
数组不是Stack类所创建的实例,所以返回false。
假设要使[]
数组是Stack类所创建的实例,返回true,我们可以重写Symbol.hasInstance的方法
console.log(Symbol()===Symbol());//false8
Symbol.iterator
Symbol.iterator
指定函数是否会返回对象的迭代器。
具有 Symbol.iterator
属性的对象称为可迭代对象。
在ES6中,Array、Set、Map和string都是可迭代对象。
ES6提供了for...of循环,它可以用在可迭代对象上。
console.log(Symbol()===Symbol());//false9
在背后,JavaScript引擎首先调用numbers
数组的 Symbol.iterator
方法来获取迭代器对象,然后它调用 iterator.next()
方法并将迭代器对象的value属性复制到num
变量中,3次迭代后,对象的done
属性为true
,循环推出。
我们可以通过Symbol.iterator
来获取数组的迭代器对象。
letfirstName=Symbol('firstname'),lastName=Symbol('lastname');0
默认情况下,一个自己定义的集合是不可以迭代的,但是我们可以用Symbol.iterator使其可迭代
letfirstName=Symbol('firstname'),lastName=Symbol('lastname');1
Symbol.isConcatSpreadable
我们可以使用concat()方法来合并两个数组
letfirstName=Symbol('firstname'),lastName=Symbol('lastname');2
我们也可以使用concat()来传入单个元素,而非数组
letfirstName=Symbol('firstname'),lastName=Symbol('lastname');3
在上面的例子中,我们将一个数组传给concat()方法时,concat()方法会将数组拓展为单个元素。但是,它会以不同的方式去对待单个原始参数,在ES6之前,我们无法更改此行为。
letfirstName=Symbol('firstname'),lastName=Symbol('lastname');4
将list对象合并到 ['Learning']
数组中,但list对象中的各个元素并没有被合并到数组中。
要在concat()时将list
对象中的元素单独添加到数组中的,我们需要将Symbol.isConcatSpreadable
属性添加到list对象中,像下面这样
letfirstName=Symbol('firstname'),lastName=Symbol('lastname');5
如果将Symbol.isConcatSpreadable
设置为false,concat()就会将list
整个对象合并到数组中。
Symbol.toPrimitive
Symbol.toPrimitive
方法决定了当一个对象被转换成原始值时的行为。
JavaScript引擎在每个类型值的原型上定义了Symbol.toPrimitive方法。
Symbol.toPrimitive
方法接受一个hint参数,该参数会是下面三个值,string、number、default,hint参数用来指定返回值的类型。hint参数由JavaScript引擎根据使用对象的上下文进行填充。
letfirstName=Symbol('firstname'),lastName=Symbol('lastname');6
其他
Symbol.match(regex)
:一个在调用 String.prototype.match()
方法时调用的方法,用于比较字符串。
Symbol.replace(regex, replacement)
:一个在调用 String.prototype.replace()
方法时调用的方法,用于替换字符串的子串。
Symbol.search(regex)
:一个在调用 String.prototype.search()
方法时调用的方法,用于在字符串中定位子串。
Symbol.species(regex)
:用于创建派生对象的构造函数。
Symbol.split
:一个在调用 String.prototype.split()
方法时调用的方法,用于分割字符串。
Symbol.toStringTag
:一个在调用 String.prototype.toString()
方法时使用的字符串,用于创建对象描述。
Symbol.unscopables
:一个定义了一些不可被 with 语句引用的对象属性名称的对象集合。
总结
今天我们学习了Symbol的所有的使用方法,以及在平时常用的一些方式。希望对你能有帮助。
如果本文有帮助,微信搜索【小帅的编程笔记】,让我们每天进步