JS对象类型转换基本类型

125 阅读2分钟

看个题

const a = {};
const b = {};
const obj = {};
obj[a] = 123;
obj[b] = 456;
console.log(obj[a]);

这是一道经典的面试题,其中涉及到了对象键值和类型转换的相关知识点,看完此文你自然会知道此题的答案

对象转boolean,string,number

const obj = {};
console.log(Boolean(obj)); // true
console.log(String(obj)); // [object Object]
console.log(Number(obj)); // NaN

从结果来看,暂时得出以下结论:

  • 对象转boolean为true
  • 对象转string为[object Object]
  • 对象转Number为NaN

那么JS内部究竟是怎样运作的呢

hint

hint是各种情况下类型转换的三种描述

string 描述

对象转字符串,就会匹配到 string hint,如:使用 alert 函数,对象作为键

const obj1 = {};
alert(obj1); // [object Object]

const obj2 = {};
obj2[obj1] = 123; // { '[object Object]': 123 }

number 描述

对象转数字,会匹配到 number hint,通常用于数学运算,一些数学内建函数会包括这种转换,如:+号后转换数字,Date日期对象

const obj = {};

// 一元运算 
const a = +obj; // NaN

// 显示转换
const b = Number(obj); // NaN

// date日期对象内建函数
const c = new Date() - new Date(); // 0 

// 大于、小于
const d = obj > obj; // false

default 描述

当运算符“不确定”期望值类型的时候会匹配到 default hint,如:二元加法,与字符串、数字、symbol进行==比较时

const obj = {};

// 二元加法
const a = obj + obj; // [object Object][object Object]

// 与number判断
obj == 1 // false

自定义转换方法

为了让用户能自定义某个对象的类型转换,JS提供了三种方法

obj[Symbol.toPrimitive](hint)

Symbol.toPrimitive 是一个是内置的 symbol 属性,指定了 hint 中的三种描述

image.png

如果此方法存在,则会被用于所有 hint

let user = {
  name: "pangyujs",
  value: 666,
  [Symbol.toPrimitive](hint) {
    console.log("hint=", hint);
    if (hint === "string") {
      return `My name is ${this.name}`;
    } else if (hint === 'number') {
      return this.value;
    } else {
      return "This is a default";
    }
  },
};

// hint: string 
console.log(String(user)) // My name is pangyujs

// hint: number
console.log(+user); // 666

// hint: default(二元表达式用到两次user,隐藏会调用两次)
console.log(user + user); // This is a default

toString()/valueOf()

如果没有 Symbol.toPrimitive,那么 JS 会尝试寻找 toStringvalueOf

  • 对于 string hint:调用 toString,如果不存在,调用 valueOf,所以转字符串的时候会优先调用 toString
  • 对于 number 和 default hint,调用 valueOf,如果不存在,调用toString,所以数学运算时,会优先调用 valueOf

默认情况下,普通对象都有这两个分法,toString 返回 [object Object],valueOf 返回自身

const obj = {};
obj.toString(); // [object Object]

obj.valueOf(); // {}

接下来改写上面 Symbol.toPrimitive 提出的例子

let user = {
  name: "pangyujs",
  value: 666,
  toString() {
    return `My name is ${this.name}`;
  },
  valueOf() {
    return this.value;
  },
};

// hint: string 
console.log(String(user)) // My name is pangyujs

// hint: number
console.log(+user); // 666

// hint: default(二元表达式用到两次user,隐藏会调用两次)
console.log(user + user); // 1332

可以说明 string hint 会优先调用 toString,number、default hint 会优先调用 valueOf

注意:如果没有 Symbol.toPrimitive 和 valueOf, toString 将处理所有类型转换

做题

const a = {};
const b = {};
const obj = {};
obj[a] = 123;
obj[b] = 456;
console.log(obj[a]);

在对象作为键的时候,会默认走 string hint,所以 a 和 b 都会转为 [object Object] 字符串,最后 b 的赋值覆盖了 a,答案为 456

控制台打印结果:

image.png

总结

对象类型转换,是由内建函数和运算符自动调用的,有三种类型描述

  1. string:对于 alert 和其他需要字符串的操作
  2. number:对于数学运算
  3. default:少数运算符,通常和 number 相同的方式转换

实际使用中,通常只实现 toString 就满足大部分需求了,像 Array,Number 等都重写了 toString 方法