关于 Symbol 的理解
基础语法
-
Symbol代表唯一值
-
Symbol 函数不能被 new
- symbol()不是 Symbol 函数的一个实例
- Symbol.prototype 与 Object(Symbol())
- 把 Symbol 变成对象实例
- Symbol.prototype 原型上有 toString,valueOf,Symbol(Symbol.toPrimitive),Symbol(Symbol.toStringTag)方法
4. Symbol 不能与其他类型的值计算
- 数学计算:不能转换为数字
- 字符串拼接:隐式转换不可以,但是可以显示转换
- 模板字符串也不能转换
5. Symbol 属性不参与 for in(of)/Object.keys/Object.getOwnPropertyNames/JSON.stringify 遍历
+ 如果要遍历对象的 Symbol 属性,需要调用 Object.getOwnPropertySymbols()方法
let obj = {
name: "珠峰培训",
age: 11,
[Symbol("A")]: 100,
[Symbol("B")]: 200,
};
let [m, n] = Object.values(obj);
console.log(m, n);//"珠峰培训",11
console.log(Object.keys(obj));//{name,age}
------------------------------------------------------------------------------------------
let obj = {
name: "珠峰培训",
age: 11,
[Symbol('A')]: 100,
[Symbol('B')]: 200
};
let [m, n] = Object.getOwnPropertySymbols(obj);
console.log(m, n)//{[Symbol('A')],[Symbol('B')]}
内置的 Symbol 值
ES6 提供很多内置的 Symbol 值,指向语言内部使用的方法
-
Symbol.hasInstance:对象的 Symbol.hasInstance 属性,指向一个内部方法,当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法
本质是 Function 的原型上有 Symbol.hasInstance 这个方法,这样所有的函数都可以调用这个方法,从而检测是否为当前实例
class person { constructor() {} } let p1 = new person(); console.log(p1 instanceof person);//true console.log(person[Symbol.hasInstance](p1));//true console.log(Object[Symbol.hasInstance]({}));//true -
Symbol.isConcatSpreadable:值为布尔值,表示该对象用于 Array.prototype.concat()时,是否可以展开
-
Symbol.iterator:拥有此属性的对象被誉为可被迭代的对象,可以使用 for…of 循环
- 所有可被迭代的对象才能用 for of 循环。可被迭代对象:拥有 Symbol.iterator 这个属性的
常见的迭代对象:map,set,array,类数组,string(他们的原型上都有 Symbol.iterator 属性)
问题:如何让普通对象可以被迭代
```javascript
let obj = {
0: 1,
1: 2,
length: 2,
//在obj中加一个Symbol.iterator,该对象就可以被迭代
[Symbol.iterator]: Array.prototype[Symbol.iterator],
};
for (let item of obj) {
console.log(item);
}
```
-
Symbol.toPrimitive: 该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值
- 对象数据类型进行转换: 1. 调用 obj[Symbol.toPrimitive](hint),前提是存在 2. 否则,如果 hint 是 "string" —— 尝试 obj.toString() 和 obj.valueOf() 3. 否则,如果 hint 是 "number" 或 "default" —— 尝试 obj.valueOf() 和 obj.toString()
面试题:实现以下效果
if (a == 1 && a == 2 && a == 3) {
console.log('OK');
}
------------------以下为实现效果-------------------------
let a={
i:1,
[Symbol.toPrimitive](hint){
switch(hint){
case "number":
return ++this.i;
case "string":
return String(this.i);
case "default":
return ++this.i;;
}
}
};
if (a == 1 && a == 2 && a == 3) {
console.log('OK');}
--------------------其他方案-----------------------------
//方案一:数据类型转换
let a = {
i: 0,
//处理三
[Symbol.toPrimitive]() {
return ++this.i;
},
};
//处理一
a.valueOf = function() {
// this -> a
return ++this.i;
};
//处理二
a.toString = function() {
// this -> a
return ++this.i;
};
//处理四
let a = [1, 2, 3];
a.toString = a.shift;
//方案二:数据劫持
// + 在全局上下文中基于var / function声明变量,相当于给window设置对应的属性 -> window.a
// + Object.defineProperty劫持对象中某个属性的获取和设置等操作
let i = 0;
Object.defineProperty(window, "a", {
get() {
return ++i;
},
});
if (a == 1 && a == 2 && a == 3) {
console.log("OK");
}
-
Symbol.toStringTag:在该对象上面调用 Object.prototype.toString 方法时,如果这个属性存在,它的返回值会出现在 toString 方法返回的字符串之中,表示对象的类型
class Person { get[Symbol.toStringTag]() { return "Person"; }} let p1 = new Person; console.log(({}).toString.call(p1)); //"[object Person]"
【面试题】:关于 symbol 的理解
1.symbol("a")可以创建唯一值
let n = Symbol(),
m = Symbol();
console.log(n === m); //false
n = Symbol("A");
m = Symbol("A");
console.log(n === m); //false
2.symbol 的应用场景:
- 对象的唯一属性:防止同名属性,及被改写或覆盖
let n = Symbol("N");
let obj = {
name: "珠峰培训",
age: 11,
[n]: 100,
};
- 宏管理(消除魔术字符串):指代码中多次出现,强耦合的字符串或数值,应避免,而使用含义清晰的变量代替(在 redux 和 vuex 必须用到)
const vote_plus = Symbol("vote_plus");
function reducer(action) {
let state = {
count: 0,
};
switch (action.type) {
case vote_plus:
state.count++;
break;
}
return state;
}
reducer({
type: vote_plus,
});
-
很多 JS 的内置原理都是基于这些 Symbol 的属性来处理的
-
Symbol.toPrimitive
-
Symbol.hasInstance
-
Symbol.toStringTag
-
Symbol.iterator
-
...
-