一、为什么ES6引入Symbol
有时候我们在项目开发的过程中可能会遇到这样的问题,我写了一个对象,而另外的同时则在这个对象里面添加了一个属性或是方法,倘若添加的这个属性或是方法是原本的对象中本来就有的,那么这个时候势必会造成冲突,那么为了防止这种冲突,ES6
就引入了Symbol
二、Symbol使用方法
Symbol是一个新的数据类型,所以不要因为Symbol的使用方法的特殊而认为它只是es6中的新的方法。它所代表的是独一无二的,即便其参数一致。
1.使用方法
Symbol的使用方法是Symbol(描述信息),其中描述信息可以为任意类型,但若是引用类型则会调用其toString方法;若为undefined,则为Symbol(),相当于不设置描述信息;若为null则为Symbol(null)。
let name = Symbol("jyy");
let name1 = Symbol("jyy");
console.log(typeof name); // "symbol"
console.log(name); // Symbol(jyy)
console.log(name === name1); // 两个Symbol不相等,false
let obj = {
toString : function(){
return "jyy"
}
};
let obj_name = Symbol(obj);
console.log(obj_name); // Symbol(jyy) 调用引用类型的toString
let unf_name = Symbol(undefined);
console.log(unf_name); // Symbol() 相当于没有设置描述信息
let null_name = Symbol(null);
console.log(null_name); // Symbol(null)
从打印出的信息,我们可以看到,其类型是symbol。而且即便在Symbol中的描述字符串设置为相同的内容,最后二者依旧是不相等的。
2.和其他数据类型的关系
我们知道数据类型之间是可以互相转化的,那么Symbol和其他的数据类型之间是如何转化的?
a. 转换为字符串
1 let name = Symbol("jyy"); 2 console.log(String(name)); // "Symbol(jyy)" 3 console.log(name.toString()); // "Symbol(jyy)" 4 console.log("" + name); // 报错TypeError: Cannot convert a Symbol value to a string
可以看出String()和toString()方法是可以转为字符串的,但是不可使用“”+Symbol()转化,回报类型错误
b.转换为布尔值
let name = Symbol("jyy"); console.log(Boolean(name)); // true if(name){ console.log(true); // 为true }else{ console.log(false); }
Symbol类型只要赋值,就一定为true
3.将对象的属性设置为Symbol类型
let name = Symbol("jyy"); let obj = { name : "beijing", [name] : "hebei" }; console.log(obj); // { name: 'beijing', [Symbol(jyy)]: 'hebei' } console.log(obj.name); // beijing console.log(obj["name"]); //beijing console.log(obj[name]); //hebei
若对象的属性为Symbol类型,则Symbol值必须放在[]中,看代码可以体会一下
4.对还有属性为Symbol的对象进行遍历
若属性为Symbol类型,则该属性不会出现在for...in、for...of中,也不会通过使用Object.keys()、Object.getOwnPropertyName()得到,若想要得到,则需要使用Object.getOwnPropertySymbol()
let name = Symbol("jyy"); let obj = { name : "beijing", [name] : "hebei" }; for(let item in obj){ console.log(item); // name 不能遍历Symbol类型属性 } console.log(Object.keys(obj)); // [ 'name' ] 不能得到Symbol类型属性 console.log(Object.getOwnPropertyNames(obj)); // [ 'name' ] 不能得到Symbol类型属性 console.log(Object.getOwnPropertySymbols(obj)); // [ Symbol(jyy) ] 只得到Symbol类型属性
从上面的代码我们可以看到有些方法可以的得到Symbol类型,有些不行,但是却没有能够完全得到所有类型的。es6中有一个新的方法是可以得到的,如下
let name = Symbol("jyy"); let obj = { name : "beijing", [name] : "hebei" }; console.log(Reflect.ownKeys(obj)); // [ 'name', Symbol(jyy) ]
三、Symbol中的方法
1.Symbol.for()
我们知道Symbo()创建的两个变量永远不会是相同的。那么如果我们需要重新使用同一个Symbol怎么办,总不能需要挨个去进行比较吧。还好,es6为我们提供了Symbol.for()方法。
参数是symbol类型的描述信息,不同于Symbol(),这个而参数只能是字符串或者是undefined,若已经创建了则返回这个symbol,否则就进行创建并将这个新的symbol返回,代码如下
let name = Symbol.for("jyy"); let name1 = Symbol.for("jyy"); console.log(name === name1); // true
请注意,我们在使用创建描述信息为jyy的变量的时候,使用的是for,而不是Symbol(),倘若使用Symbol()进行首次创建,for会再次创建一次,二者不会相等,代码如下:
let name = Symbol("jyy"); let name1 = Symbol.for("jyy"); console.log(name === name1); // false
原因在于Symbol.for()会有一个登记机制,使用for只会对通过for创建的symbol进行检查,不会对Symbol()创建的进行检查。
2. Symbol.keyFor()
这个方法参数是一个通过Symbol.for()创建的symbol类型变量,返回这个symbol变量的描述信息。
let name = Symbol.for("jyy"); console.log(Symbol.keyFor(name)); // "jyy"
let name1 = Symbol("jyy");
console.log(Symbol.keyFor(name1)); // undefined 不能查找Symbol()创建的变量
四、内置的Symbol属性
1.Symbol.hasInstance
这个属性只想一个内部方法,当该对象使用instanceof时,会调用这个方法,代码如下:
let Animal = {
[Symbol.hasInstance](foo){
return true
}
}
let obj = {};
console.log(obj instanceof Animal); // true
另外写下对于原始instaceoOf的原理:
function instanceOf(L,R){ var R_temp = R.prototype; // 获取右侧对象的原型对象 L = L.__proto__; // 获取左侧对象的原型对象 while(true){ if(L == null){// 若左侧对象的原型对象是空,则返回false return false; } if(L === R_temp){ // 若二者的原型对象相等,则说明L是由R直接或间接创建的 return true; } L = L.__proto__; // 通过原型链不断向上原型对象 } }
2.Symbol.isConcatSpreadable
这个属性为一个布尔值,用来表示该对象是否在Array.prototype.concat()时可以展开,干说不懂,代码如下:
let arr = [1,2,3]; let arr1 = [4,5]; console.log(arr1[Symbol.isConcatSpreadable]); // undefined console.log(arr.concat(arr1)); // [ 1, 2, 3, 4, 5 ], 数组的Symbol.isConcatSpreadable默认为undefined,可以展开(true更可以) arr1[Symbol.isConcatSpreadable] = false; console.log(arr.concat(arr1)); // [ 1, 2, 3, [ 4, 5, [Symbol(Symbol.isConcatSpreadable)]: false ] ] 设置为false后不可展开
3.Symbol.match
这个属性指向一个方法,当执行str.match(obj)时,会调用这个函数并返回其返回值,代码如下:
class Person{ [Symbol.match](num){ return parseInt(num) > 100; // 自定义方法内的逻辑 } } console.log("123".match(new Person())); // true console.log("12".match(new Person())); // false
4.Symbol.replace
这个属性指向一个方法,当执行str.replace(obj)时,会调用这个方法并返回其返回值,代码如下:
class Person{ [Symbol.replace](obj,rst){ // 两个参数,第一个是对象,第二个是返回的结果 console.log(rst); // wss return "hello"; // 自定义返回值 } } console.log("jyy".replace(new Person(), "wss")); // hello
5.Symbol.toPrimitive
这个属性指向一个方法,当对象被转为原始类型时,会调用这个方法,代码如下
let Person = { [Symbol.toPrimitive](type){ // 参数为三种: string,number,default switch(type){ case "string": return "jyy"; case "number": return 28; default: return "Beijing" } } }; console.log("my name: " + String(Person)); //my name: jyy console.log("my age: " + Number(Person)); // my age: 28 console.log("my address is: " + Person); // my address is: Beijing
6.Symbol.toStringTag
个人感觉这个方法还是比较实用的,最早看到这这个属性的使用是在webpack3打包后的js代码中,用来自定义一个方法的toString()值。我们知道当我们在aler一个对象的时候,会显示[object Object],这个属性指向的方法就是可以将Object换为自定义。代码如下:
let person = { [Symbol.toStringTag] : "Person" } console.log(person.toString()); // [object Person]