js中对象,数组转原始类型

140 阅读4分钟

分类

js中转换为原始类型,分3种情况:

  1. 转换为 Number
  2. 转换为 String
  3. 转换为 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 !!!~~~

image.png

数组/对象不存在 [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 !!!

image.png

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 ~~~

image.png

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 ~~~

image.png

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 !!!

image.png

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)); // 报错

image.png

小结

对象、数组转换为 Number/String 的过程中,若对象/数组自身含有[Symbol.toPrimitive]函数,则直接执行该函数并返回结果;若对象/数组自身不含有[Symbol.toPritimive]函数,则转换为Number的话,会执行valueOf函数,若返回结果不是原始类型数据,则再执行toString函数,返回结果仍不是原始类型数据的话,则报错;转换为String,会执行toString函数,若返回结果不是原始类型数据,则再执行valueOf函数,若返回结果仍不是原始类型数据,则报错。

简单的总结,有不足之处,不严谨之处,请不吝赐教~!