为什么在JavaScript类型转换中对象转字符串时,ToPrimitive方法中还有个调用valueOf方法判断是否为原始值的步骤

203 阅读8分钟

最近也是一直在学习JavaScript中的一些较为基础的知识,今天在学习到类型转换的时候,突然有一点小小的疑惑,想向各位大佬请教一下,就是我们将对象转换为字符串时,ToPrimitive方法中我们执行了toString方法后都不能获得返回值,那使用valueOf方法更不能获得返回值,那为什么这个步骤要存在呢,不能直接删掉吗?有没有大佬不吝赐教的。

先分享一下我今天学习到的JavaScript中的类型转换的内容

Javascript 是一种弱类型语言,也就是说它里面的变量是没有明确类型的,而是由 JavaScript 引擎在编译时隐式完成。在 JavaScript 中,我们将一种值类型转换为另一种值类型,叫做类型转换。可能我们平时没有太注意过类型转换,但是它在我们平时写的代码中是无处不在的,熟悉JavaScript中的类型转换对我们学好这门语言还是有很大的帮助的。我们前面已经知道了,JavaScript中的诗句类型分为两大类,一种是原始数据类型,一种是引用数据类型。

所有我们可以将数据类型之间的转换可以分为下面三种:

  1. 原始数据类型之间的转换。
  2. 原始数据类型转换为对象。
  3. 引用数据类型转换为原始数据类型。

原始数据类型之间的转换

1、转换为布尔类型

转换为布尔类型我个人总结出来一点点,就是凡是一些不符合咱们正常认知的都是false。例如falsenullundefinedNaN0''这6个值通过Boolean()转换为布尔类型都是false,其他的值转换成布尔类型都是true。如果我还有遗漏的可以在评论区给我介绍一下,嘿嘿嘿。

image.png

2、转换为String类型

任何原始类型的值通过String()转换为String类型的值都是将其值转换为字符串,如下:

  1. 数字 (Number) :

    • 整数或浮点数会被转换为其对应的文本表示,例如 123 转换为 "123"3.14 转换为 "3.14"
    • Infinity 和 -Infinity 转换为 "Infinity" 和 "-Infinity"。(无穷大或无穷小)
    • NaN 转换为 "NaN"
  2. 布尔值 (Boolean) :

    • true 转换为 "true"
    • false 转换为 "false"
  3. 空值 (Null) :

    • null 转换为 "null"
  4. 未定义 (Undefined) :

    • undefined 转换为 "undefined"
  5. 字符串 (String) :

    • 已经是字符串的值保持不变。

3、转换为Number类型

凡是认为不能转成Number类型的,如不是单纯数字的字符串's123',以及undefined,转换成Number都是NaN,对于布尔类型的true转换为1,false转换为0,Null转换成Number也是0,Number类型的转换还是数字。

image.png

原始数据转换为引用数据类型

这个就是我们常说的包装类,使用各自的包装类,来实现将原始数据转换为对象(引用类型)。例如:let numObj = new Number(42)let strObj = new String("Hello")let boolObj = new Boolean(true)。我们当然可以使用跟原始数据类型类似的方法使用Object()转换成对象。 例如:

console.log(Object('fqd'));//[String: 'fqd'] 
console.log(Object(123));//[Number: 123] 
console.log(Object(true));//[Boolean: true]

我们常用的原始数据类型中只有这三个类型有各自的包装类,那么常用的剩下那两个呢?当然也可以转换为对象,只不过不适用包装类,而是使用Object(null),Object(undefined)转换成对象,最后的结果都是一个空对象{}

以上这两只类型转换都不是今天的主角,他们在实际的应用中也不是很大,但是我们需要对其有一定的了解。在平时用到的时候我们可以直接查表,这样可以大大的减轻我们的负担。

image.png

这是我随机在百度找到的类型转换表

引用类型转换为原始类型

将引用类型数据类型转换为原始类型也分为三种,分别为:

  • 将子对象转换为Number类型
  • 将对象转换为String类型
  • 将对象转换为布尔类型

这三种的转换机制还是有所差异的。

1、转换为Number类型

当需要将对象转换为Number类型时,Javascript 首先调用ToNumber(x),这个函数又会调用 ToPrimitive()将其转换为原始类型,然后将派生的原始类型转换为Number。

ToPrimitive(obj,Number)过程:

  1. 判断接收到的值是不是原始类型,是则返回
  2. 否则,调用valueOf方法(只能操作包装类,将包装类转化为原始值)如果得到了就返回原始值
  3. 否则,调用toString方法,如果得到了原始值,则返回
  4. 如果前三者都没得到,就报错

这是我通过官方文档对于ToPrimitive的介绍(es5.github.io/#x9.1),加上自己的一些整理出来的ToPrimitive(obj,Number)过程。

这样可能有点不太好理解,那我们来分析一下这个例子 我们将一个空对象转换成数字类型。console.log(Number({}))

  • 首先我们的JavaScript先调用ToNumber()方法。
  • 然后调用ToPrimitive(),先判断{}不是原始值。
  • 然后再调用valueOf()方法,看能不能将其转换为原始值,而我们知道的valueOf()只能将包装类转换为原始值,显然这一步也不会得到原始值。
  • 只好再进行第三步,使用toString()方法。这时候我们就返回了这个'[object,Object]'值,
  • 最后就是将原始值'[object,Object]'转换为Number,所以就返回NaN

这就是我们分析出来的结果,与js执行出来的结果是一样的。

image.png

2、转换为String类型

其实这个转换过程和Number类型很类似,只是转换成String类型。其中的ToPrimitive(obj,Number)过程也有一些差异,第二步和第三步交换了一下位置。

具体的过程如下:

当需要将对象转换为Number类型时,Javascript 首先调用ToNumber(x),这个函数又会调用 ToPrimitive()将其转换为原始类型,然后将派生的原始类型转换为Number。

ToPrimitive(obj,Number)过程:

  1. 判断接收到的值是不是原始类型,是则返回
  2. 否则,调用toString方法,如果得到了原始值,则返回
  3. 否则,调用valueOf方法(只能操作包装类,将包装类转化为原始值)如果得到了就返回原始值
  4. 如果前三者都没得到,就报错
这里有点疑惑,之所以要兑换一下位置,是因为这样可以提高转换速度。但是我们执行了toString方法后都不能获得返回值,那使用valueOf方法更不能获得返回值,那为什么这个步骤要存在呢,不能直接删掉吗?有没有大佬不吝赐教的。

3、转换为布尔类型

这个是最简单的,因为任何对象转布尔一定是true

toString方法其实也有很多讲究

  1. Object.prototyoe.toString() 返回一个'[object,xxxx]'
  2. Array.prototype.toString 返回一个由数组内部的元素以逗号拼接的字符串
  3. xxxx.prototype.toString 返回'原始值'

...所有的类型上面都打了toString 只有Array,和Object特别一点。

我们的类型转化又分为隐式转换和显示转换。

显式转换

显式转换(Explicit Conversion)是指在代码中明确地使用转换函数或操作符来将一个数据类型转换为另一个数据类型。这种转换是人为明确指定的,以确保代码的行为符合预期

隐式转换

隐式转换(Implicit Conversion)是指在 JS 中,在某些运算或操作中,当涉及到不同类型的数据时,JavaScript 引擎会自动进行类型转换,以便完成该运算或操作。这种转换是由 JavaScript 引擎隐式地执行的,而不需要人们明确指定。

[]==![]就是很经典的一个隐式转换。 首先这里面存在的两个运算符,一个是判断是否相等的运行符,另一个就是取反的运算符。而!的优先级比==高。所以是先进行这个操作![],变成false==进行隐式类型转换成数字类型,所有false转换为数字为0[]转换为数字类型也是0,所以最后的结果就是true

一元操作符 + (只有一个元素)
如:+ '1' 得到1  因为加号会触发隐式类型转换  往Number
二元运算符 +
如:1+'1'  {}+[], 1+[]

这些也都进行了隐式类型转换

总结

在这篇文章中主要介绍了一下JavaScript中的类型转换问题,另外还向大佬询问了我的一个疑惑。如果大佬能给我解惑一下,我感激不尽!可能我这问题有点迷惑,有点太小白了,因为我肯=可能有点钻牛角尖的嫌疑,大佬别喷我!!!