分类
js中转换为原始类型,分3种情况:
- 转换为 Number
- 转换为 String
- 转换为 Boolean
原理
数组,在js中本质上也是对象,从 typeof([]) // "object" 即可看出。而对象转换为原始类型,最简单的就是转换为 Boolean ,结果都是 true。而转换为 Number 以及 String ,则复杂一些,视具体情况而定,不过还是有规律的:
数组,对象转换为 Number/String 涉及到三个函数,分别是 valueOf,toString,[Symbol.toPrimitive],具体优先级等情况用以下伪代码说明:
function typeTransform(data, type){
// 不是转为为 Number, String 类型则直接返回
if(!["Number", "String"].includes(type)) return;
let res = undefined;
// [Symbol.toPrimitive] 函数优先级最高,有则执行并返回结果
if(data[Symbol.toPrimitive]){
res = data[Symbol.toPrimitive]();
}else{
/* 此时视具体要转换为 Number 还是 String 而决定 valueOf 和 toString 函数的执行顺序
* 1. 若转换为 Number ,则执行 valueOf 函数,执行结果不是原始类型数据的话,
则执行 toString 函数,若结果仍不是原始数据类型,则报错。
* 2. 若转换为 String,则执行 toString 函数,执行结果不是原始类型数据的话,
则执行 valueOf 函数,若结果仍不是原始类型数据,则报错。
*/
res = (type === "Number") ? data.valueOf() : data.toString();
if(/* res 不是原始类型数据 */){
res = (type === "Number") ? data.toString() : data.valueOf();
if(/* res 不是原始类型数据 */){
throw Error("TypeError: Cannot convert to primitive value")
}
}
}
return res;
}
验证
数组/对象存在 [Symbol.toPrimitive] 函数
1. 目的:分别转换为Number以及String
前提:数组/对象含有 [Symbol.toPrimitive] 函数
预期结果:直接获取 [Symbol.toPrimitive] 函数的执行结果并返回
// 创建含有 [Symbol.toPrimitive] 函数的对象.
let obj = {
valueOf(){
console.log("[Object] valueOf !!!");
return 0;
},
toString(){
console.log("[Objcet] toString ~~~");
return 1;
},
[Symbol.toPrimitive](){
console.log("[Objcet] toProtimive ~~~!!!");
return 2;
}
};
// 创建含有 [Symbol.toPrimitive] 函数的数组
let arr = [];
arr.valueOf = function(){
console.log("[Array] valueOf !!!");
return 0;
};
arr.toString = function(){
console.log("[Array] toString ~~~");
return 1;
};
arr[Symbol.toPrimitive] = function(){
console.log("[Array] toPrimitive !!!~~~");
return 2;
}
// 转换为Number
console.log(Number(obj)); // 2 - [Objcet] toProtimive ~~~!!!
console.log(Number(arr)); // 2 - [Array] toPrimitive !!!~~~
// 转换为String
console.log(String(obj)); // 2 - [Objcet] toProtimive ~~~!!!
console.log(String(arr)); // 2 - [Array] toPrimitive !!!~~~
数组/对象不存在 [Symbol.toPrimitive] 函数
1. valueOf 函数以及 toString 函数返回结果为原始类型数据
1. 目的:转换为Number
前提:valueOf 函数返回结果为原始数据类型
预期结果:直接获取 valueOf 函数的执行结果并返回
// 创建不含有 [Symbol.toPrimitive] 函数的对象.
let obj = {
valueOf(){
console.log("[Object] valueOf !!!");
return 0;
},
toString(){
console.log("[Objcet] toString ~~~");
return 1;
},
};
// 创建不含有 [Symbol.toPrimitive] 函数的数组
let arr = [];
arr.valueOf = function(){
console.log("[Array] valueOf !!!");
return 0;
};
arr.toString = function(){
console.log("[Array] toString ~~~");
return 1;
};
// 转为为Number
console.log(Number(obj)); // 0 - [Object] valueOf !!!
console.log(Number(arr)); // 0 - [Array] valueOf !!!
2. 目的:转换为 String
前提:toString 函数返回结果为原始类型数据
预期结果:直接获取 toString 函数的结果并返回
// 创建不含有 [Symbol.toPrimitive] 函数的对象.
let obj = {
valueOf(){
console.log("[Object] valueOf !!!");
return 0;
},
toString(){
console.log("[Objcet] toString ~~~");
return 1;
},
};
// 创建不含有 [Symbol.toPrimitive] 函数的数组
let arr = [];
arr.valueOf = function(){
console.log("[Array] valueOf !!!");
return 0;
};
arr.toString = function(){
console.log("[Array] toString ~~~");
return 1;
};
// 转为为String
console.log(String(obj)); // 1 - [Objcet] toString ~~~
console.log(String(arr)); // 1 - [Array] toString ~~~
2. valueOf 函数或者 toString 函数返回的不是原始类型数据
1. 目的:转换为 Number
前提:valueOf 函数返回结果不是原始类型数据, toString 函数返回的是原始类型数据
预期结果:先执行 valueOf 函数,再执行 toString 函数
// 创建不含有 [Symbol.toPrimitive] 函数的对象.
let obj = {
valueOf(){
console.log("[Object] valueOf !!!");
return [];
},
toString(){
console.log("[Objcet] toString ~~~");
return 1;
},
};
// 创建不含有 [Symbol.toPrimitive] 函数的数组
let arr = [];
arr.valueOf = function(){
console.log("[Array] valueOf !!!");
return [];
};
arr.toString = function(){
console.log("[Array] toString ~~~");
return 1;
};
// 转换为 Number
console.log(Number(obj)); // 1 - [Object] valueOf !!! ===> [Objcet] toString ~~~
console.log(Number(arr)); // 1 - [Array] valueOf !!! ===> [Array] toString ~~~
2. 目的:转换为 String
前提:toString 函数返回结果不是原始类型数据,valueOf 函数返回结果是原始类型数据
预期结果:先执行 toString 函数,再执行 valueOf 函数
// 创建不含有 [Symbol.toPrimitive] 函数的对象.
let obj = {
valueOf(){
console.log("[Object] valueOf !!!");
return 0;
},
toString(){
console.log("[Objcet] toString ~~~");
return {};
},
};
// 创建不含有 [Symbol.toPrimitive] 函数的数组
let arr = [];
arr.valueOf = function(){
console.log("[Array] valueOf !!!");
return 0;
};
arr.toString = function(){
console.log("[Array] toString ~~~");
return [];
};
// 转换为 String
console.log(String(obj)); // 0 - [Objcet] toString ~~~ ===> [Object] valueOf !!!
console.log(String(arr)); // 0 - [Array] toString ~~~ ===> [Array] valueOf !!!
3. 目的:分别转换为 Number / String
前提:valueOf 函数以及 toString 函数返回的均不是原始类型数据
预期结果:报错
// 创建不含有 [Symbol.toPrimitive] 函数的对象.
let obj = {
valueOf(){
console.log("[Object] valueOf !!!");
return [];
},
toString(){
console.log("[Objcet] toString ~~~");
return {};
},
};
// 创建不含有 [Symbol.toPrimitive] 函数的数组
let arr = [];
arr.valueOf = function(){
console.log("[Array] valueOf !!!");
return [];
};
arr.toString = function(){
console.log("[Array] toString ~~~");
return [];
};
console.log(Number(obj)); // 报错
console.log(Number(arr)); // 报错
console.log(String(obj)); // 报错
console.log(String(arr)); // 报错
小结
对象、数组转换为 Number/String 的过程中,若对象/数组自身含有[Symbol.toPrimitive]函数,则直接执行该函数并返回结果;若对象/数组自身不含有[Symbol.toPritimive]函数,则转换为Number的话,会执行valueOf函数,若返回结果不是原始类型数据,则再执行toString函数,返回结果仍不是原始类型数据的话,则报错;转换为String,会执行toString函数,若返回结果不是原始类型数据,则再执行valueOf函数,若返回结果仍不是原始类型数据,则报错。
简单的总结,有不足之处,不严谨之处,请不吝赐教~!