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

栏目: JavaScript · 发布时间: 6年前

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

在读 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
复制代码

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

ToPrimitive

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

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

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

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

  1. 检查对象中是否有用户显式定义的 [Symbol.toPrimitive] 方法,如果有,直接调用;
  2. 如果没有,则执行原内部函数 ToPrimitive ,然后判断传入的 hint 值,如果其值为 string ,顺序调用对象的 toStringvalueOf 方法(其中 toString 方法一定会执行,如果其返回一个基本类型值,则返回、终止运算,否则继续调用 valueOf 方法);
  3. 如果判断传入的 hint 值不为 string ,则就可能为 number 或者 default 了,均会顺序调用对象的 valueOftoString 方法(其中 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 可取的三种值,都有什么含义?又什么情况对应什么值?

确定 hint 取值

string
当在希望是字符串操作,也即发生对象到字符串的转换时,传入内部函数 ToPrimitive 的参数值即为 string

// output
alert(obj);

// using object as a property key
anotherObj[obj] = 123;
复制代码

number
当在希望是数值操作,也即发生对象到数值的转换时,传入内部函数 ToPrimitive 的参数值即为 number

// explicit conversion
let num = Number(obj);

// maths (except binary plus)
let n = +obj; // unary plus
let delta = date1 - date2;

// less/greater comparison
let greater = user1 > user2;
复制代码

default
当在一些不确定需要将对象转换成什么基础类型的场景下,传入内部函数 ToPrimitive 的参数值即为 default

// binary plus
let total = car1 + car2;

// obj == string/number/symbol
if (user == 1) { ... };
复制代码

结语

如果亲爱的读者们在本文中发现了什么错误,或者有什么不同的意见,还请留言,一起讨论,一起将隐藏的、晦涩的点提出来,然后解决掉。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Bad Blood

Bad Blood

John Carreyrou / Knopf / 2018-5-21 / USD 27.95

The full inside story of the breathtaking rise and shocking collapse of Theranos, the multibillion-dollar biotech startup, by the prize-winning journalist who first broke the story and pursued it to t......一起来看看 《Bad Blood》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

html转js在线工具
html转js在线工具

html转js在线工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具