什么是Symbol
ES5的对象属性名都是字符串,很容易造成属性名冲突,这就需要一种机制实现属性名的独一无二,这就是Symbol要解决的问题。
Symbol是ES6新增的原始数据类型,表示独一无二的值,需要通过Symbol函数生成。
Symbol基础使用
Symbol函数接受一个字符串参数,表示对Symbol的描述,意味着参数相同的Symbol是不同的值。
如果参数是一个对象,就会调用该对象的toString方法。
var s1 = Symbol('a');
var s2 = Symbol('a');
var s3 = Symbol('b');
console.log(s1 == s2);//false
console.log(s1.toString(),s2.toString(),s3.toString());//Symbol(a),Symbol(a),Symbol(b)
var obj = {
toString:function(){
return 'abc';
}
}
console.log(Symbol(obj))//Symbol(abc);
运算
Symbol不能与其他类型的值进行运算,但是,Symbol可以显示转为字符串和布尔值,但是不能转为数值。
var symbol = Symbol('a');
"a" + symbol; //TypeError:can't convert symbel to string;
symbol.toString(); //Symbol(a)
Boolean(symbol); //true
Number(symbol); //Error
作为属性名
构建唯一的属性名是Symbol的主要功能,这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被覆盖。
var symbol = Symbol();
var a = {};
a[symbol] = "Hellow";
var a = {
[symbol] : "Hellow"
};
var a = {};
Object.defineProperty(a,symbol,{value : "Hellow"});
以上三种赋值语句都会得到相同的结果,定义和提取属性的时候要注意点运算符
都会将其理解成字符串,所以要用[]
引导。
遍历
Symbol作为属性名的属性是不会出现在普通的遍历中,需要使用Object.getOwnPropertySymbols
方法返回所有属性名是symbol构成的symbol数组。
var obj = {};
var symbolA = Symbol('a');
var symbolB = Symbol('b');
obj[symbolA] = "Hellow";
obj[symbolB] = "World";
var symbols = Object.getOwnPropertySymbols(obj);
console.log(symbols ); //[Symbol(a),Symbol(b)]
还有一个APIReflect.ownKeys
可以返回所有类型的键名,包括常规键名和Symbol键名:
let obj = {
[Symbol()] : 1,
a : 2,
b : 3
}
console.log(Reflect.ownKeys(obj));//[Symbol(),'a','b']
复用的Symbol
Symbol()
方法每次调用都会创建一个新的Symbol
值,如果需要使用同一个Symbol
,可以使用Symbol.for()
方法。这个方法接受一个字符串作为参数,然后搜索有没有以这个参数作为名称的Symbol
值,有则返回,没有则在全局环境中登记该Symbol
。
Symbol.for("bar") === Symbol.for("bar"); //true
Symbol.for("bar") === Symbol("bar"); //false
Symbol("bar") === Symbol("bar"); //false
iframe = document.createElement("iframe");
iframe.src = String(window.location);
document.body.appendChild(iframe);
iframe.contentWindow.Symbol.for("bar") === Symbol.for("bar");//true
Symbol.keyFor()
能回一个已登记的Symbol类型值得key
,而没有对应的key
在登记列表中的symbol 类型会返回undefined:
var symbolByFor = Symbol.for("foo");
var symbol = Symbol("foo");
console.log(symbolByFor,symbol);//"foo",undefined
内置的Symbol方法
Symbol.hasInstance
当其他对象使用instanceof
运算符的时候判断是否该对象的实例时,会调用这个方法;
比如:foo instanceof Foo
在语言内部,实际是这样的Foo[Symbol.hasInstance](foo)
。
class MyClass{
[Symbol.hasInstance](foo){
console.log("Symbol.hasInstance");
return foo instanceof Array;
}
}
console.log([] instanceof new MyClass());
/*
console:
Symbol.hasInstance
true
*/
//
直接和原型进行判断:
class MyClass{
static [Symbol.hasInstance](foo){
console.log("Symbol.hasInstance");
return foo instanceof Array;
}
}
console.log([] instanceof MyClass);
/*
console:
Symbol.hasInstance
true
*/
//
Symbol.isConcatSpreadable
表示该对象使用Array.prototype.concat()
时,是否可以展开:
let arr1 = ["c","d"];
console.log(["a","b"].concat(arr1,"e"));//["a","b","c","d","e"]
console.log(arr1[Symbol.isConcatSpreadable]);//undefined
let arr2 = ["c","d"];
arr1[Symbol.isConcatSpreadable]=false;
console.log(["a","b"].concat(arr1,"e"));//["a","b",["c","d"],"e"]
数组的默认行为是可以展开的,Symbol.isConcatSpreadable
属性等于true
或undefined
都会开启展开;
类似数组的对象也可以展开,但它的Symbol.isConcatSpreadable
属性默认为false
,必须手动打开:
let obj= {length : 2, 0 : "c" , 1 : "d"};
console.log(["a","b"].concat(obj,"e"));//["a","b",obj,"e"]
obj[Symbol.isConcatSpreadable]=true;
console.log(["a","b"].concat(obj,"e"));//["a","b","c","d","e"]
Symbol.species
指向当前对象的构造函数,创造实例时,默认会调用这个方法,即使用这个属性返回的函数当作构造函数,来创造新对象。