是一种数据类型、不是对象。表示独一无二的值,其他6中数据类型:Undefined、Null、Boolean、String、Number、Object。
let p = Symbol();
typeof p //"symbol"
Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述,用来区分相同名称的Symbol值。
let p = Symbol();
let o = Symbol();
p //Symbol();
o //Symbol();
let p = Symbol("p1");
p //Symbol("p1");
1.Symbol值不能与其他类型的值进行运算,会报错
2.Symbol值可以显示转换为字符串。
String(p); //Symbol(“p1”);
p.toString(); //Symbol(“p1”);
3.Symbol值可以转为布尔值,不能转为数值。Boolean(p); //true
Symbol值作为对象的属性名
使用Symbol值定义属性时,必须放在括号中。由Symbol定义的属性是公开的,不是私有属性。
var p = Symbol();
//1.
var a = {};
a[p] = 'hello';
//2.
var a = {
[p]:'hello'
}
//3.
Object.defineProperty(a,p,{value:'hello'});
对象的属性名不能使用点运算符‘.’,点运算符后总是字符串。
Symbol定义常量,不用担心后续会定义相同的值。
消除魔术字符串(某些与代码形成强耦合的字符串和数值),把这些魔术字符串写成变量。
Symbol的属性名遍历
用Symbol值作为属性名不会被for…in、for…of、Object.keys()、Object.getOwnPropertyNames()返回。
1.Object.getOwnPropertySymbols()返回数组。
2.Reflect.ownKeys()返回所有类型的键名。
利用Symbol值的遍历特点,定义一些非私有的内部方法的效果。
Symbol.for():接收一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值。有则返回;无则创建,会登记在全局中作为搜索对象。
Symbol.keyFor():以一个Symbol值变量为参数,返回一个已经存在的Symbol类型值的key。
//1.
var s = Symbol.for("p");
var p = Symbol.for("p");
s===p //true
//2.返回已登记的Symbol值
var t = Symbol.keyFor(p); //undefined
var p = Symbol.for("p");
Symbol.keyFor(p); //"p"
Symbol.for()登记的值是全局的,在不同的iframe或service worker中取得同一个值。
内置的Symbol值
1.Symbol.hasInstance属性指向一个内部方法,判断是否为某个构造函数的实例;对象使用instanceof运算符会调用这个方法。
2.Symbol.isConcatSpreadable属性等于布尔值,表示对象使用Array.prototype.concat()时时候是否可以展开;
//Symbol.isConcatSpreadable属性默认true,可以展开
let arr = ['a','b'];
['c','d'].concat(arr,'e'); //['c','d','a','b','e']
//类似数组的对象 Symbol.isConcatSpreadable属性默认false,需手动打开
let obj = {length:2,0:'a',1:'b'};
['c','d'].concat(obj,'e'); //['c','d',obj,'e']
obj[Symbol.isConcatSpreadable] = true;
['c','d'].concat(obj,'e'); //['c','d','a','b','e']
3.Symbol.iterator属性指向默认遍历器方法,for…of循环会调用这个方法。
4.Symbol.species属性指向一个方法,对象作为构造函数创造实例时会调用。
即this.constructor[Symbol.species]存在,就会使用这个属性作为构造函数来创造新实例对象。
5.Symbol.match指向一个函数,这个属性存在时,执行str.match(myObject)返回匹配的值。
6.Symbol.replace属性指向一个方法,replace();
7.Symbol.search属性指向一个方法,search();
8.Symbol.split属性指向一个方法,split();
9.Symbol.toPrimitive属性指向一个方法,对象被转为原始类型的值时会调用这个方法,返回该对象对应的原始类型值。
10.Symbol.toStringTag属性指向一个方法,执行toString方法返回的字符串中,表示对象的类型。
11.Symbol.unscopables属性指向一个对象,使用with关键字时哪些属性会被排除在外。
next()、return()、throw()
Iterator只是把接口规格加到数据结构上,遍历器和所遍历的数据结构实际上是分开的。
具有原生的Iterator接口:数组、类似数组的对象、Set、Map
1.Symbol.iterator属性上部署Iterator
class RangeIterator{
constructor(start,stop){
this.value = start;
this.stop = stop;
}
[Symbol.iterator](){
return this;
}
next(){
var value = this.value;
if(value<this.stop){
this.value++;
return {done:false,value:value};
}else{
return {done:true,value:undefined};
}
}
}
function range(start,stop){
return new RangeIterator(start,stop);
}
for(var value of range(0,3)){
console.log(value);
}
2.对象添加Iterator接口
let obj = {
data:['hello','word'],
[Symbol.iterator](){
const self = this;
let index = 0;
return{
next(){
if(indexreturn{
value:self.data[index++],
done:false
};
}else{
return {value:undefined,done:true};
}
}
}
}
}
3.类似数组的对象(存在数值键名和length属性),直接引用数组的Iterator接口。
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
//
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];
//
let obj = {
1:"hello",
3:"world",
length:2,
[Symbol.iterator]:[][Symbol.iterator]
}
//1.以数值作为属性名时,按下标0开始,如果属性名超出length,遍历时取不到值length后的值。
for(let item of obj){
console.log(item); //undefined,"hello"
}
有了遍历器接口,数据结构可以使用for…of,也可以使用while循环。
解构赋值、扩展运算符、yield*会默认调用Symbol.iterator方法。
for…of /Array.from()/Map()/WeakMap()/Set()/WeakSet()/Promise.all()/Promise.race()
字符串是一个类似数组的对象。
遍历器对象的return(),throw()
return()方法:for…of循环提前退出(出错、break语句、continue语句),或者一个对象在遍历完成后需要清理或释放资源,可部署return()方法。
**return()方法必须返回一个对象
for…in循环,只能获得对象的键名,不能直接获取键值;可以直接循环遍历对象的键名。可以使用Object.keys()方法将对象的键名生成一个数组,然后遍历这个数组。
for(var key of Object.keys(someObject)){
console.log(key+":"+someObject[key]);
}
for…in 也会遍历手动添加的其他键,包括原型链上的键。以任意顺序遍历键名。