xx19941215 / light-tips

Some code tips about algorithms, php and more 🔥
MIT License
718 stars 143 forks source link

Javascript引擎内部的三种抽象操作 #9

Open xx19941215 opened 7 years ago

xx19941215 commented 7 years ago

很久之前我在知乎上问过一道很无聊的Js题目,链接在这里JS中{}+[]和[]+{}的返回值情况是怎样的?,这篇文章要说的和这个题目有一些关系。下面介绍一下Js引擎内部的三种抽象操作。

1.通过ToPrimitive()将值转换为原始值。 JavaScript引擎内部的抽象操作ToPrimitive()有着这样的签名: ToPrimitive(input, PreferredType)

可选参数PreferredType可以是Number或者String,它只代表了一个转换的偏好,转换结果不一定必须是这个参数所指的类型,但转换结果一定是一个原始值.如果PreferredType被标志为Number,则会进行下面的操作来转换输入的值:

①如果输入的值已经是个原始值,则直接返回它.否则,如果输入的值是一个对象.则调用该对象的valueOf()方法.如果valueOf()方法的返回值是一个原始值,则返回这个原始值.否则,调用这个对象的toString()方法.如果toString()方法的返回值是一个原始值,则返回这个原始值.否则,抛出TypeError异常.

②如果PreferredType被标志为String,则转换操作的第二步和第三步的顺序会调换.如果没有PreferredType这个参数,则PreferredType的值会按照这样的规则来自动设置:Date类型的对象会被设置为String,其它类型的值会被设置为Number.

2.通过ToNumber()将值转换为数字 下面的表格解释了ToNumber()是如何将原始值转换成数字的

ToNumber()转换规则 如果输入的值是一个对象,则会首先会调用ToPrimitive(obj, Number)将该对象转换为原始值,然后在调用ToNumber()将这个原始值转换为数字.转型函数Number()会根据此规则执行

3.通过ToString()将值转换为字符串 下面的表格解释了ToString()是如何将原始值转换成字符串

ToString()转换规则

如果输入的值是一个对象,则会首先会调用ToPrimitive(obj, String)将该对象转换为原始值,然后再调用ToString()将这个原始值转换为字符串.转型函数String()会根据该规则执行

理解了Javascript引擎内部的三种抽象操作之后可以看下面的例子:

小例子

这里的转型函数Number会根据2的规定来执行类型转换,由于重写了对象的valueOf和toString函数(之前toString会返回"[object Object]",valueOf会返回该对象),所以Number()根据规则首先调用对象的valueOf方法,没有获得原始值,继续调用toString方法,任然没有获得原始值,这时就会抛出TypeError。

再来看一个例子:即{}+[]和[]+{}的返回值是怎么样? 对于{}+[] 我们将{}理解为一个空的代码块,所以问题就变成了 +[] 的结果,这里的加号并不是代表加法的二元运算符,而是一个一元运算符,作用是将它后面的操作数转换成数字,和Number()函数作用完全一样,即求Number([])的结果是多少?[]继承自Array,Array对象的的valueOf方法直接返回该数组并不是原始值,继续调用toString方法,得到""即一个空的字符串。那么Number("")的结果就是0,所以{}+[]的结果就是0。而[]+{}的结果呢?你可以参考《Javascript高级程序设计》的3.5.5加性操作符一章和本文的相关说明获得答案是"[object Object]"。

文章的最后总结一些类型分别调用valueOf、toString和toLocaleString得到的结果: 相关总结