1. 基础使用
1.1 Symbol概述
// ES6新引入的原始数据类型,表示独一无二的值,是类似于字符串的数据类型。
//通过 Symbol函数生成 可作为对象的属性名
let s=Symbol();
console.log(s); // Symbol()
console.log(typeof s); // Symbol
// Symbol不得使用new 因为Symbol不是对象 是原始类型的值
// 由于不是对象,所以也不能添加属性
//这个括号中的参数是描述,用于区分Symbol,否则每个Symbol都一样 不利于区分
let s1=Symbol('foo');
let s2=Symbol('bar');
console.log(s1); // Symbol(foo)
console.log(s2); // Symbol(bar)
console.log(s1.toString()); // "Symbol(foo)" 注意:控制台打印的是Symbol 但是其实是字符串形式
//若传入的参数是一个对象 先调用对象的toString方法转成字符串再生成Symbol对象,默认情况下对象的toString返回的是"[object Object]"
const obj={
toString(){
return 'abc'
}
};
const sym=Symbol(obj);
console.log(sym);
//Symbol中 参数只是描述,所以相同参数的返回值还是不一样的
//无参情况
let s3=Symbol();
let s4=Symbol();
console.log(s3 === s4);
//相同参数情况
let s5=Symbol('foo');
let s6=Symbol('foo');
console.log(s5 === s6);
//Symbol不可以和其他类型的值进行运算 但是可以转成字符串 String(Symbol())
//Symbol可以转换成布尔值 但是不能转换成数值
1.2 Symbol作为属性名
var mySymbol=Symbol();
//第一种写法
var obj1={};
obj1[mySymbol]='hello';
//第二种写法
var obj2={
[mySymbol]:'hello'
}
//第三种写法
var obj3={};
Object.defineProperty(obj3,mySymbol,{value:"hello"})
//结果都是 {Symbol(): "hello"}
//Symbol作为对象属性名时不能使用点运算符。
var mySymbol2=Symbol();
var obj4={};
// 注意 使用 对象名.属性名 的方式这里的属性名是字符串类型
obj4.mySymbol2='hello';
console.log(obj4[mySymbol2]); // undefined
console.log(obj4['mySymbol2']); // hello
//在对象的内部中,使用Symbol定义属性时,Symbol值必须放在[]中
let symbol=Symbol();
let obj={
[symbol]:function () {
return 1;
},
// [symbol](){return 1; } 简写
};
console.log(obj[symbol]());
1.3 Symbol作为属性名如何遍历
//通过getOwnPropertySymbols获取属性名为Symbol类型的
let obj={};
let a=Symbol('a');
let b=Symbol('b');
obj[a]='hello';
obj[b]='world';
obj['c']='Symbol';
let objectSymbols=Object.getOwnPropertySymbols(obj);
console.log(objectSymbols); // 获取的是数组 [ Symbol(a), Symbol(b) ]
//Object.getOwnPropertySymbols()、for...in循环以及Object.getOwnPropertyNames对比
let obj2={};
let foo=Symbol('foo');
obj2[foo]='Symbol'
//for...in获取不到Symbol的属性的值
for (let i in obj2){
console.log('这里的代码不会执行 因为for..in访问不到Symmbol的值'); // 不会执行
}
//getOwnPropertyNames获取不到Symbol属性的值
console.log(Object.getOwnPropertyNames(obj2)); // []
//Symbol属性可以通过getOwnPropertySymbols获取
console.log(Object.getOwnPropertySymbols(obj2)); // [ Symbol(foo) ]
//可以通过新的API——Reflect.ownKeys()方法获取所有键名
let obj3={
[Symbol('symbol')]:'Symbol',
str:'字符串',
};
console.log(Reflect.ownKeys(obj3)); // [ 'str', Symbol(symbol) ]
//以Symbol值作为名称的属性不会被常规的方法遍历得到,可以利用这个特性定义一些非私有的又只希望只用于内部的方法
let serect=Symbol('serect');
class Test{
[serect]='自定义私有属性';
}
let t=new Test()
console.log(t.serect); // 报错 无法通过.的方式访问
console.log(t[serect]); // 可以访问
// 使用Symbol作为属性名 并不是真正的私有 而是不容易访问到 可以用作私有
1.4 Symbol.for()和Symbol.keyFor()的使用
// 刚刚提到了Symbol()即便参数相同 产生的Symbol的值也是不同的 那么如果产生相同的值?
// 这时就要使用到Symbol.for()方法
// Symbol.for()方法可以创建的Symbol值是相同的,因为使用Symbol.for()创建时会被登记在全局环境中以供搜索,而使用Symbol不会。
// Symbol.for()方法可以创建的Symbol值是相同的,因为使用Symbol.for()创建时会被登记在全局环境中以供搜索,而使用Symbol不会。
let s1=Symbol.for('foo');
let s2=Symbol.for('foo');
console.log(s1 === s2); // true
console.log(Symbol.for('bar') === Symbol.for('bar')); // true
console.log(Symbol('bar') === Symbol('bar')); // false
let s3=Symbol('foo'); // 没使用Symbol.for() 不在全局环境中
// Symbol.keyFor()只能找到被Symbol.for()登记到全局环境中的值
console.log(Symbol.keyFor(s1)); // foo
console.log(Symbol.keyFor(s2)); // foo
console.log(Symbol.keyFor(s3)); // undefined
2. 内置的Symbol的值
内置的值还有很多 这里简要的介绍两个比较好用的,具体想了解,可以看一下阮老师的书。 内置的 Symbol 值
2.1 Symbol.hasInstance
//Symbol.hasInstance属性:指向一个内部方法,对象使用instanceof运算符时会调用这个方法,返回Symbol.hasInstance指向的内部方法的值。若没有自定义Symbol.hasInstance 默认返回的是 判断该对象是否为某个构造函数的实例。
//mc instanceof MyClass就会运行 MyClass[Symbol.hasInstance](foo)方法
let test={};
// 判断 test是否是Object的实例对象
console.log(Object[Symbol.hasInstance](test)); // true
// 判断 test是否是String的实例对象
console.log(String[Symbol.hasInstance](test)); // false
// 例一:自定义[Symbol.hasInstance](){}方法
class MyClass{
constructor(){
}
static [Symbol.hasInstance](foo){
// 这个返回的值 就是使用instanceof的结果
return foo.constructor.name===MyClass.name;
}
}
let mc=new MyClass();
console.log(mc instanceof MyClass); // true
// 例二:自定义[Symbol.hasInstance](){}方法 判断是否为偶数
class Even{
static [Symbol.hasInstance](obj){
// 这个返回的值 就是使用instanceof的结果
return Number(obj)%2===0;
}
}
console.log(1 instanceof Even); // false
console.log(2 instanceof Even); // true
2.2 Symbol.isConcatSpreadable
// 对象的Symbol.isConcatSpreadable属性等于一个布尔值,表示该对象使用Array.prototype.concat()时是否可以展开。
// 默认情况下,Symbol.isConcatSpreadable都是undefined
// 数组默认就可以展开
let arr1=['c','d'];
console.log(arr1[Symbol.isConcatSpreadable]); // undefined,因为是数组 所以可以展开
let arr2=['a','b'].concat(arr1,'e');
console.log(arr2); // [ 'a', 'b', 'c', 'd', 'e' ] 使用concat方法继续拼接
// 类似数组结构的对象,需要将isConcatSpreadable设置为true方可展开
// 不是类似数组结构的对象 设置为true 也没有用
let obj={0:'a',1:'b',length:2}; // 这就是类似数组的对象
let arr3=['c','d'].concat(obj,'e'); // 不将isConcatSpreadable设置为true 并进行拼接
console.log(arr3); // ['c','d',{ '0': 'a', '1': 'b', length: 2 },'e'] 可以看到obj还是以对象的形式展示
obj[Symbol.isConcatSpreadable]=true; // 将isConcatSpreadable设置为true
let arr4=['c','d'].concat(obj,'e'); // 进行拼接
console.log(arr4); // ['c','d','a','b','e'] 对象obj除length之外的值都被拆分出来进行拼接了