原来 JS 还存在这样的拆箱转换
栏目: JavaScript · 发布时间: 5年前
内容简介:在读 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 复制代码
可以看到, toString
和 valueOf
的执行顺序并不固定,而是根据某个条件来决定的,那么是根据什么呢?那就是在拆箱转换时,调用了对象的 ToPrimitive
内部函数时,其会根据执行上下文,自动传入一个转换类型参数,暂时给它命名为 hint
。
ToPrimitive
在 JavaScript 标准中,规定了 ToPrimitive
函数,它是对象类型到基本类型转换的实现者(即,拆箱转换);但这是一个内部算法,是编程语言在内部执行时遵循的一套规则。
对象到 String 和 Number 的转换都遵循“先拆箱再转换”的规则。通过拆箱转换,把对象变成基本类型,再从基本类型转换为对应的 String 或者 Number。
但是对于不同的操作,拆箱转换的内部实现也有所区别,正如上面的例子所示。
「拆箱转换」的调用规则及顺序如下:
-
检查对象中是否有用户显式定义的
[Symbol.toPrimitive]
方法,如果有,直接调用; -
如果没有,则执行原内部函数
ToPrimitive
,然后判断传入的hint
值,如果其值为string
,顺序调用对象的toString
和valueOf
方法(其中toString
方法一定会执行,如果其返回一个基本类型值,则返回、终止运算,否则继续调用valueOf
方法); -
如果判断传入的
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
可取的三种值,都有什么含义?又什么情况对应什么值?
确定 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) { ... }; 复制代码
结语
如果亲爱的读者们在本文中发现了什么错误,或者有什么不同的意见,还请留言,一起讨论,一起将隐藏的、晦涩的点提出来,然后解决掉。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
深入浅出WebAssembly
于航 / 电子工业出版社 / 2018-11 / 128.00元
WebAssembly是一种新的二进制格式,它可以方便地将C/C++等静态语言的代码快速地“运行”在浏览器中,这一特性为前端密集计算场景提供了无限可能。不仅如此,通过WebAssembly技术,我们还可以将基于Unity等游戏引擎开发的大型游戏快速地移植到Web端。WebAssembly技术现在已经被计划设计成W3C的标准,众多浏览器厂商已经提供了对其MVP版本标准的支持。在Google I/O ......一起来看看 《深入浅出WebAssembly》 这本书的介绍吧!