ES6引入的新的原始数据类型Symbol,表示独一无二的值,它是js中的第7种数据类型,是一种类似于字符串的数据类型。
Symbol特点:
1、Symbol的值是唯一的,用来解决命名冲突的问题。
2、Symbol值不能与其他数据进行运算。
3、Symbol定义的对象属性不能使用for...in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名。然后遍历。
如何创建?
可以使用函数创建。
let s = Symbol('demo');
或者
let s1 = Symbol.for('demo');
使用for函数可以得到某个字符串唯一的symbol值,例如下面的比较是相等的。
let s2 = Symbol.for('demo');
s1 === s2 返回的就是true。
Symbol内置的值
用做对象的属性。通过对这些内置值的设置,来改变对象在某些特定场景下的表现。
最常用的有:
- Symbol.hasInstance
在使用instanceof操作符时调用。
class Person{
static [Symbol.hasInstance](param){
console.log(param);
console.log('我被用来检查类型了')
return false;
}
}
let obj = {}
console.log(obj instanceof Person)
- Symbol.isConcatSpreadable
使用concat来合并两个数组的时候,js会默认将数组内的元素展开然后合并为一个数组返回(原数组不变),这个行为es6以后可以使用Symbol.isConcatSpreadable改变,将其置为false就可以将数组作为一整个对象合并到过去。
let animals = ['cat','dog','panda']
let fruit = ['apple','pear']
fruit[Symbol.isConcatSpreadable] = true
console.log(animals.concat(fruit)) // 结果是['cat','dog','panda','apple','pear']
fruit[Symbol.isConcatSpreadable] = false
console.log(animals.concat(fruit)) // 结果是['cat','dog','panda', Array(2)]
- Symbol.iterator
用于给对象定义迭代方法,这样就可以使用es6中新提供的for...of语法,需要注意的是,自定义的这个迭代器方法,需要按照一定的规范来写,需要返回一个具有next方法的对象,next方法需要返回具有value和done两个属性的对象。done会被用来判断什么时候结束迭代。
const banji = {
name: "终极一班",
stus: [ 'xiaoming', 'xiaoning', 'xiaotian', 'knight' ],
[Symbol.iterator]() {
// 索引变量
let index = 0;
// 保存this
let _this = this;
return {
next: function() {
if (index < _this.stus.length) {
const result = { value: _this.stus[index], done: false };
// 下标自增
index++;
// 返回结果
return result;
} else {
return { value: undefined, done: true };
}
}
};
}
}
// 遍历这个对象
for (let v of banji) {
console.log(v);
}
- Symbol.match
一个在调用 String.prototype.match() 方法时调用的方法, 直白点说就是如果你定义一个字符串strValue,当你调用strValue.match(ObjectCase),如果ObjectCase这个对象定义了Symbol.match方法时,就会执行这个方法。
let animal = {
[Symbol.match](str){
console.log(str) //打印出字符串 cat
return str.indexOf('animal');
}
}
let kitty = "cat"
console.log(kitty.match(animal)) //返回值-1,这里打印出-1
- Symbol.replace
一个在调用 String.prototype.replace() 方法时调用的方法,和上面的一样,当一个字符串调用strValue.replace(ObjectCase)时,会去执行ObjectCase中的Symbol.replace方法。
let animalName = {
/* strValue 会返回调用者本身,replacement为调用repalce方法时传入的第二个参数 */
[Symbol.replace](strValue, replacement){
console.log("strValue: " + strValue,"replacement: " + replacement) //strValue: cat, its name is {params} replacement: {params}
return strValue.replace("{params}", "kitty")
}
}
let kitty = "cat, its name is {params}"
console.log(kitty.replace(animalName, "{params}")) //cat, its name is kitty
- Symbol.search
一个在调用 String.prototype.search() 方法时调用的方法,用于在字符串中定位子串,这个也和上面str相关扩展一样直接看示例。
let animalName = {
/* strValue就是调用者本身 */
[Symbol.search](strValue){
console.log(strValue) //cat, its name is kitty
return strValue.split("name is")[1]
}
}
let kitty = "cat, its name is kitty"
console.log(kitty.search(animalName)) //kitty
- Symbol.split
一个在调用 String.prototype.split() 方法时调用的方法,用于分割字符串。
let animalName = {
/* strValue就是调用者本身 */
[Symbol.split](strValue){
console.log(strValue) //cat, its name is kitty
return strValue.indexOf("name")
}
}
let kitty = "cat, its name is kitty"
console.log(kitty.split(animalName)) //8
- Symbol.toPrimitive
这个方法决定了当一个对象被转换为原始值时的行为。JavaScript引擎在每个类型值的原型上定义了Symbol.toPrimitive方法。这个方法被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式:
number: 该场景下需要转换成数值.对应操作符:* / -
string: 该场景下需要转换成字符串,显示使用string的构造方法
default:该场景下可以转换成数值,也可以转换成字符串,对应操作符:+
let obj = {
[Symbol.toPrimitive](hint){
switch(hint){
case 'number':
return 123;
case 'string':
return 'str';
case 'default':
return 'default';
default:
throw new Error();
}
}
}
console.log(2 * obj) //246
console.log(3 + obj) //3default
console.log(obj == 'default') //true
console.log(String(obj)) //str
- Symbol.species
对象的Symbol.species属性,指向一个构造函数,创建衍生对象时,会使用该属性。这个的主要用途是,有些类库是在基类的基础上修改的,那么子类使用继承的方法时,作者可能希望返回基类的实例,而不是子类的实例。
class MyArray extends Array {
static get [Symbol.species](){
return Array;
}
}
//b,c是a的衍生对象
const a = new MyArray(1,2,3)
const b = a.map(x => x+1)
const c = a.filter(x => x > 0)
//如果没有定义上面的Symbol.species,那么下面的instanceof均返回true
console.log(b instanceof MyArray) //true
console.log(c instanceof MyArray) //true
//定义了上面的Symbol.species时,返回值是下面这样
console.log(b instanceof MyArray) //false
console.log(b instanceof Array) //true
- Symbol.toStringTag
一个在调用 String.prototype.toString() 方法时使用的字符串,用于创建对象描述。在对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串中,表示对象的类型。
//第一种写法,注意这里的get关键字是必须的,call方法需要用到get取值器
let animal = {
get [Symbol.toStringTag](){
return 'animal'
}
}
console.log(Object.prototype.toString.call(animal)) // [object animal]
//第二种写法
console.log({[Symbol.toStringTag]: 'tag'}.toString()) // [object tag]
- Symbol.unscopables
一个定义了一些不可被 with 语句引用的对象属性名称的对象集合。集合中的属性名称,会在with环境绑定中被排除。
const animal = {
type: 'animal',
name: 'kitty'
}
with(animal){
console.log(type, name) //animal kitty
}
//定义Symbol.unscopables以便在with环境中排除某些属性
animal[Symbol.unscopables] = {
type: true
}
with(animal){
console.log(type) //Uncaught ReferenceError: type is not defined
}
以上这些被称为众所周知的symbols,其实其中很多值并不常用,为了验证这些symbols的准确用法,我搜了很多资料,总算把每一个都亲自验证了一遍。
这里附上一个地址,这里面有特别详细的示例代码,我也是自己费了半天劲儿快验证完的时候才发现这个网址的,白白浪费了好多时间。
Symbol比较全面的示例
另外,之所以要研究这个,主要是最近在学vue3 + typescript,身为一个没太多基础的前端小白,学习的过程中遇到一个知识点可能都会要查半天资料才能弄懂,不过我也乐在其中。
网友评论