专栏名称: 程序猿何大叔
前端工程师
目录
相关文章推荐
前端早读课  ·  【第3464期】从初级开发者到高级开发者:借 ... ·  18 小时前  
前端之巅  ·  npm 够用吗?初创企业为何追捧这个 ... ·  2 天前  
前端早读课  ·  【图书】零基础开发AI ... ·  2 天前  
前端早读课  ·  【第3462期】7 分钟深度理解柯里化 ·  2 天前  
51好读  ›  专栏  ›  程序猿何大叔

原来 JS 还存在这样的拆箱转换

程序猿何大叔  · 掘金  · 前端  · 2019-01-29 09:57

正文

阅读 198

原来 JS 还存在这样的拆箱转换

在读 Winter 大佬的《重学前端》栏目时,重温了 JS 的「拆箱转换」。「装箱转换」与「拆箱转换」以前都是了解的,今天来看,自己所谓的了解也真是一知半解。在阅读 Winter 老师写的内容后,对「拆箱转换」这个知识点还是不甚清楚,因此我再去深入地了解一番,参考资料详见文末的「参考链接」。

被我们忽略的表象

首先,我们来看一下例子:

const a = {
    name: 'a',
    toString () {
        console.log(this);
        console.log('toString');
        return { name: 'toString' };
    },
    valueOf () {
        console.log(this);
        console.log('valueOf');
        return { name: 'valueOf' };
    }
};

a * 2;
// {name: "a", toString: ƒ, valueOf: ƒ}
// valueOf
// {name: "a", toString: ƒ, valueOf: ƒ}
// toString
// Uncaught TypeError: Cannot convert object to primitive value

a + "";
// {name: "a", toString: ƒ, valueOf: ƒ}
// valueOf
// {name: "a", toString: ƒ, valueOf: ƒ}
// toString
// Uncaught TypeError: Cannot convert object to primitive 

alert(a);
// {name: "a", toString: ƒ, valueOf: ƒ}
// toString
// {name: "a", toString: ƒ, valueOf: ƒ}
// valueOf
// Uncaught TypeError: Cannot convert object to primitive value
复制代码

可以看到, toString valueOf 的执行顺序并不固定,而是根据某个条件来决定的,那么是根据什么呢?那就是在拆箱转换时,调用了对象的 ToPrimitive 内部函数时,其会根据执行上下文,自动传入一个转换类型参数,暂时给它命名为 hint

ToPrimitive

在 JavaScript 标准中,规定了 ToPrimitive 函数,它是对象类型到基本类型转换的实现者(即,拆箱转换);但这是一个内部算法,是编程语言在内部执行时遵循的一套规则。

对象到 String 和 Number 的转换都遵循“先拆箱再转换”的规则。通过拆箱转换,把对象变成基本类型,再从基本类型转换为对应的 String 或者 Number。

但是对于不同的操作,拆箱转换的内部实现也有所区别,正如上面的例子所示。

「拆箱转换」的调用规则及顺序如下:

  1. 检查对象中是否有用户显式定义的 [Symbol.toPrimitive] 方法,如果有,直接调用;
  2. 如果没有,则执行原内部函数 ToPrimitive ,然后判断传入的 hint 值,如果其值为 string ,顺序调用对象的 toString valueOf 方法(其中 toString 方法一定会执行,如果其返回一个基本类型值,则返回、终止运算,否则继续调用 valueOf 方法);
  3. 如果判断传入的 hint 值不为 string ,则就可能为 number 或者 default 了,均会顺序调用对象的 valueOf toString 方法(其中 valueOf 方法一定会执行,如果其返回一个基本类型值,则返回、终止运算,否则继续调用 toString 方法);

来看一下第一种情况:

const b = {
    [Symbol.toPrimitive] (hint) {
        console.log(`hint: ${hint}`);
        return {};
    },
    toString () {
        console.log('toString');
        return 1;
    },
    valueOf () {
        console.log('valueOf');
        return 2;
    }
};

alert(b); // hint: string 
b + ''; // hint: default
b + 500; // hint: default
+b; // hint: number
b * 1; // hint: number
复制代码

第二、三种情况:

const c = {
    toString () {
        console.log('toString');
        return 1;
    },
    valueOf () {
        console.log('valueOf');
        return 2;
    }
};

alert(c); // 打印出 toString 并 alert 出 1
c + '';  // 先后打印出 valueOf,"2"
c + 500; // 先后打印出 valueOf,502
+c; // 先后打印出 valueOf,2
c * 1; // 先后打印出 valueOf,2
复制代码

那么关于 hint 可取的三种值,都有什么含义?又什么情况对应什么值?







请到「今天看啥」查看全文