17道题让你彻底理解JS中的类型转换

前言

类型转换是将值从一种类型转换为另一种类型的过程(比如字符串转数字,对象转布尔值等)。任何类型不论是原始类型还是对象类型都可以进行类型转换,JavaScript 的原始类型有:number, string, boolean, null, undefined, Symbol。

本文将通过 17 道题目来深入的了解 JS 中的类型转换,通过阅读本文之后,你将能自信的回答出下面题目的答案,并且能够理解背后的原理。在文章的最后,我讲写出答案并解释。在看答案之前,你可以把答案写下来,最后再对照一下,便于找出理解有误的地方。

true + false
12 / "6"
"number" + 15 + 3
15 + 3 + "number"
[1] > null
"foo" + + "bar"
"true" == true
false == "false"
null == ""
!!"false" == !!"true"
["x"] == "x"
[] + null + 1
[1,2,3] == [1,2,3]
{} + [] + {} + [1]
! + [] + [] + ![]
new Date(0) - 0
new Date(0) + 0

类似于上面的这些问题大概率也会在 JS 面试中被问到, 所以继续往下读。

隐式 vs 显式类型转换

类型转换可以分为隐式类型转换和显式类型转换。

当开发人员通过编写适当的代码(如Number(value))用于在类型之间进行转换时,就称为显式类型强制转换(或强制类型转换)。

然而 JavaScript 是弱类型语言,在某些操作下,值可以在两种类型之间自动的转换,这叫做隐式类型转换。在对不同类型的值使用运算符时通常会发生隐式类型转换。比如 1 == null, 2 / "5", null + new Date() 。当值被 if 语句包裹时也有可能发生,比如 if(value) {} 会将 value 转换为 boolean类型。

严格相等运算符(===)不会触发类型隐式转换,所以它可以用来比较值和类型是否都相等。

隐式类型转换是一把双刃剑,使用它虽然可以写更少的代码但有时候会出现难以被发现的bug。

三种类型转换

我们需要知道的第一个规则是:在 JS 中只有 3 种类型的转换

  • to string
  • to boolean
  • to number

第二,类型转换的逻辑在原始类型和对象类型上是不同的,但是他们都只会转换成上面 3 种类型之一。

我们首先分析一下原始类型转换。

String 类型转换

String() 方法可以用来显式将值转为字符串,隐式转换通常在有 + 运算符并且有一个操作数是 string 类型时被触发,如:

String(123) // 显式类型转换

123 + '' // 隐式类型转换

所有原始类型转 String 类型

String(123) // '123'
String(-12.3) // '-12.3'
String(null) // 'null'
String(undefined) // 'undefined'
String(true) // 'true'

Symbol 类型转 String 类型是比较严格的,它只能被显式的转换

String(Symbol('symbol')) // 'Symbol(symbol)'

'' + Symbol('symbol') // TypeError is thrown

Boolean 类型转换

Boolean() 方法可以用来显式将值转换成 boolean 型。

隐式类型转换通常在逻辑判断或者有逻辑运算符时被触发(|| && !)。

Boolean(2) // 显示类型转换
if(2) {} // 逻辑判断触发隐式类型转换
!!2  // 逻辑运算符触发隐式类型转换
2 || 'hello' // 逻辑运算符触发隐式类型转换

注意: 逻辑运算符(比如 || 和 &&)是在内部做了 boolean 类型转换,但实际上返回的是原始操作数的值,即使他们都不是 boolean 类型。

// 返回 number 类型 123,而不是 boolean 型 true
// 'hello' 和 '123' 仍然在内部会转换成 boolean 型来计算表达式
let x = 'hello' && 123 // x === 123

boolean 类型转换只会有 true 或者 false 两种结果。

Boolean('')  // false
Boolean(0)  // false
Boolean(-0)  // false
Boolean(NaN)  // false
Boolean(null)  // false
Boolean(undefined) // false
Boolean(false) // false

任何不在上面列表中的值都会转换为 true, 包括 object, function, Array, Date 等,Symbol 类型是真值,空对象和空数组也是真值。

Boolean({})  // true
Boolean([])  // true
Boolean(Symbol()) // true
!!Symbol()  // true
Boolean(function() {}) // true

Number 类型转换

和 Boolean()、String() 方法一样, Number() 方法可以用来显式将值转换成 number 类型。
number 的隐式类型转换是比较复杂的,因为它可以在下面多种情况下被触发。

  1. 比较操作(>, <, <=, >=)
  2. 按位操作(| & ^ ~)
  3. 算数操作(- + * / %), 注意,当 + 操作存在任意的操作数是 string 类型时,不会触发 number 类型的隐式转换
  4. 一 元 + 操作
  5. 非严格相等操作(== 或者 !== ),注意,== 操作两个操作数都是 string 类型时,不会发生 number 类型的隐式转换
Number('123') // 显示类型转换
+ '123'  // 隐式类型转换
123 != "456" // 隐式类型转换
4 > "5" // 隐式类型转换
5 / null // 隐式类型转换
true | 0 // 隐式类型转换

接下来看一下原始类型显示转换 number 类型会发生什么

Number(null)   // 0
Number(undefined)  // NaN
Number(true)   // 1
Number(false)   // 0
Number(" 12 ")   // 12
Number("-12.34")  // -12.34
Number("\n")   // 0
Number(" 12s ")  // NaN
Number(123)   // 123

当将一个字符串转换为一个数字时,引擎首先删除前尾空格、\n、\t 字符,如果被修剪的字符串不成为一个有效的数字,则返回 NaN。如果字符串为空,则返回 0。

Number() 方法对于 null 和 undefined 的处理是不同的, null 会转换为 0, undefined 会转换为 NaN

不管是显式还是隐式转换都不能将 Symbol 类型转为 number 类型,当试图这样操作时,会抛出错误。

Number(Symbol('my symbol')) // TypeError is thrown
+Symbol('123')   // TypeError is thrown

这里有 2 个特殊的规则需要记住:

1、当将 == 应用于 null 或 undefined 时,不会发生数值转换。null 只等于 null 或  undefined,不等于其他任何值。

null == 0  // false, null is not converted to 0
null == null  // true
undefined == undefined // true
null == undefined // true
undefined == 0  // false

2、NaN 不等于任何值,包括它自己

NaN === NaN // false

if(value !== value) { console.log('the value is NaN') }

object 类型转换

到这里我们已经深入了解了原始类型的转换,接下来我们来看一下 object 类型的转换。

当涉及到对象的操作比如:[1] + [2,3],引擎首先会尝试将 object 类型转为原始类型,然后在将原始类型转为最终需要的类型,而且仍然只有 3 种类型的转换:number, string, boolean

最简单的情况是 boolean 类型的转换,任何非原始类型总是会转换成 true,无论对象或数组是否为空。

对象通过内部 [[ToPrimitive]] 方法转换为原始类型,该方法负责数字和字符串转换。

[[ToPrimitive]] 方法接受两个参数一个输入值和一个需要转换的类型(Numer or String)

number 和 string的转换都使用了对象的两个方法: valueOf 和 toString。这两个方法都在 Object.prototype 上被声明,因此可用于任何派生类,比如 Date, Array等。

通常上 [[ToPrimitive]] 算法如下:

  • 如果输入的值已经是原始类型,直接返回这个值。
  • 输入的值调用 toString() 方法,如果结果是原始类型,则返回。
  • 输入的值调用 valueOf() 方法,如果结果是原始类型,则返回。
  • 如果上面 3 个步骤之后,转换后的值仍然不是原始类型,则抛出 TypeError 错误。

number 类型的转换首先会调用 valueOf() 方法,如果不是原始值在调用 toString() 方法。 string 类型的转换则相反。

大多数 JS 内置对象类型的 valueOf() 返回这个对象本身,其结果经常被忽略,因为它不是一个原始类型。所以大多数情况下当 object 需要转换成 number 或 string 类型时最终都调用了 toString() 方法。

当运算符不同时,[[ToPrimitive]] 方法接受的转换类型参数也不相同。当存在 == 或者 + 运算符时一般会先触发 number 类型的转换再触发 string 类型转换。

在 JS 中你可以通过重写对象的 toString 和 valueOf 方法来修改对象到原始类型转换的逻辑。

答案解析

接下来我们按照之前的转换逻辑来解释一下每一道题,看一下是否和你的答案一样。

true + false // 1

'+' 运算符会触发 number 类型转换对于 true 和 false

12 / '6' // 2

算数运算符会把字符串 ‘6' 转为 number 类型

"number" + 15 + 3 // "number153"

'+' 运算符按从左到右的顺序的执行,所以优先执行 “number” + 15, 把 15 转为 string 类型,得到 “number15” 然后同理执行 “number15” + 3

15 + 3 + "number" // "18number"

15 + 3 先执行,运算符两边都是 number 类型 ,不用转换,然后执行 18 + “number” 最终得到 “18number”

[1] > null // true

==> '1' > 0
==> 1 > 0
==> true

比较运算符 > 执行 number 类型隐式转换。

"foo" + + "bar" // "fooNaN"

==> "foo" + (+"bar")
==> "foo" + NaN
==> "fooNaN"

一元 + 运算符比二元 + 运算符具有更高的优先级。所以 + bar表达式先求值。一元加号执行字符串“bar” 的 number 类型转换。因为字符串不代表一个有效的数字,所以结果是NaN。在第二步中,计算表达式'foo' + NaN。

'true' == true // false

==> NaN == 1
==> false

'false' == false // false

==> NaN == 0
==> false

== 运算符执行 number 类型转换,'true' 转换为 NaN, boolean 类型 true 转换为 1

null == '' // false

null 不等于任何值除了 null 和 undefined

!!"false" == !!"true" // true

==> true == true
==> true

!! 运算符将字符串 'true' 和 'false' 转为 boolean 类型 true, 因为不是空字符串,然后两边都是 boolean 型不在执行隐式转换操作。

['x'] == 'x' // true

== 运算符对数组类型执行 number 转换,先调用对象的 valueOf() 方法,结果是数组本身,不是原始类型值,所以执行对象的 toString() 方法,得到字符串 'x'

[] + null + 1 // 'null1'

==> '' + null + 1
==> 'null' + 1
==> 'null1'

'+' 运算符执行 number 类型转换,先调用对象的 valueOf() 方法,结果是数组本身,不是原始类型值,所以执行对象的 toString() 方法,得到字符串 '', 接下来执行表达式 '' + null + 1。

0 || "0" &// {}

==> (0 || '0') &==> (false || true) && true
==> true && true
==> true

逻辑运算符 || 和 && 将值转为 boolean 型,但是会返回原始值(不是 boolean)。

[1,2,3] == [1,2,3] // false

当运算符两边类型相同时,不会执行类型转换,两个数组的内存地址不一样,所以返回 false

{} + [] + {} + [1] // '0[object Object]1'

==> +[] + {} + [1]
==> 0 + {} + [1]
==> 0 + '[object Object]' + '1'
==> '0[object Object]1'

所有的操作数都不是原始类型,所以会按照从左到右的顺序执行 number 类型的隐式转换, object 和 array 类型的 valueOf() 方法返回它们本身,所以直接忽略,执行 toString() 方法。 这里的技巧是,第一个 {} 不被视为 object,而是块声明语句,因此它被忽略。计算从 +[] 表达式开始,该表达式通过toString()方法转换为空字符串,然后转换为0。

! + [] + [] + ![] // 'truefalse'

==> !(+[]) + [] + (![])
==> !0 + [] + false
==> true + [] + false
==> true + '' + false
==> 'truefalse'

一元运算符优先执行,+[] 转为 number 类型 0,![] 转为 boolean 型 false。

new Date(0) - 0 // 0

==> 0 - 0
==> 0

'-' 运算符执行 number 类型隐式转换对于 Date 型的值,Date.valueOf() 返回到毫秒的时间戳。

new Date(0) + 0

==> 'Thu Jan 01 1970 02:00:00 GMT+0200 (EET)' + 0
==> 'Thu Jan 01 1970 02:00:00 GMT+0200 (EET)0'

'+' 运算符触发默认转换,因此使用 toString() 方法,而不是 valueOf()。

总结

查看原文

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

(0)

相关推荐

  • Javascript 强制类型转换函数

    1. Boolean(value):把值转换成Boolean类型: 2. Nnumber(value):把值转换成数字(整型或浮点数): 3. String(value):把值转换成字符串. 我们先来看Boolean():在要转换的值为"至少有一字符的字符串"."非0的数字"或"对象",那么Boolean()将返回true,如果要转换的值为"空字符串"."数字0"."undefined"

  • JS 类型转换常见方法小结

    一.类型转换 1.转换成字串 ECMAScript的Boolean值.数字和字串的原始值的有趣之处在于它们是伪对象,这意味着它们实际上具有属性和方法. 如: Js代码 复制代码 代码如下: var sColor = "blue"; alert(sColor.length);//outputs "4" var sColor = "blue"; alert(sColor.length);//outputs "4" 总而言之,3种主

  • js类型转换与引用类型详解(Boolean_Number_String)

    一.类型转换 1.转换成字串 ECMAScript的Boolean值.数字和字串的原始值的有趣之处在于它们是伪对象,这意味着它们实际上具有属性和方法. 如: 复制代码 代码如下: var sColor = "blue";alert(sColor.length);//outputs "4" 总而言之,3种主要的原始值Boolean值.数字和字串都有toString()方法.ECMAScript定义的所有对象都有toString()方法,无论它是伪对象,还是真的对象.

  • Javascript将string类型转换int类型

    郁闷的事来了,先看前台HTML: 复制代码 代码如下: 购买数量: <input id="txtNum" type="text" value="1" runat="server" onchange="javascript:checkNum();" /> 件(库存<span id="getGoodsNum"> <%=GOODSNUM%></span

  • Javascript Boolean、Nnumber、String 强制类型转换的区别详细介绍

    下面就来详细说一说 Javascript 中 Boolean.Nnumber.String 强制类型转换的区别. 我们知道 Boolean(value) 是把值转换成Boolean类型,Nnumber(value) 是把值转换成数字(整型或浮点数),而 String(value) 是把值转换成字符串. 先来分析下Boolean,Boolean在转换值为"至少有一字符的字符串"."非0的数字"或"对象"的情况下返回true:在转换值为"空

  • js 变量类型转换常用函数与代码[比较全]

    1. 转换函数: js提供了parseInt()和parseFloat()两个转换函数.前者把值转换成整数,后者把值转换成浮点数.只有对String类型调用这些方法,这两个函数才能正确运行:对其他类型返回的都是NaN(Not a Number). 在判断字符串是否是数字值前,parseInt()和parseFloat()都会仔细分析该字符串.parseInt()方法首先查看位置0处的 字符,判断它是否是个有效数字:如果不是,该方法将返回NaN,不再继续执行其他操作.但如果该字符是有效数字,该方法

  • Javascript 类型转换方法

    Javascript中的变量同样支持自由类型转换成为适用(或者要求)的内容以便于使用. 弱类型的Javascript不会按照程序员的愿望从实际的变量类型到所需要的数据类型转换,例如一个非常常见的错误,在浏览器脚本中,从表单控件中获取用户将要输入的一个数值类型的变量与另一个数值变量的和.因为变量类型在表单控件中是字符串类型(计时字符串序列包含一个数字)这种尝试将会添加那个字符串到变量,即使这些值碰巧是一些数字,结果在第二个变量将会被转换为字符串类型,在最后只会把从表单控件中得到的变量添加到第一个字

  • 简单介绍JavaScript数据类型之隐式类型转换

    JavaScript的数据类型分为六种,分别为null,undefined,boolean,string,number,object.object是引用类型,其它的五种是基本类型或者是原始类型.我们可以用typeof方法打印来某个是属于哪个类型的.不同类型的变量比较要先转类型,叫做类型转换,类型转换也叫隐式转换.隐式转换通常发生在运算符加减乘除,等于,还有小于,大于等.. typeof '11' //string typeof(11) //number '11' < 4 //false 本章节单

  • JavaScript中数据类型转换总结

    在js中,数据类型转换分为显式数据类型转换和隐式数据类型转换. 1, 显式数据类型转换 a:转数字: 1)Number转换: 代码: var a = "123"; a = Number(a); 注意: a) 如果转换的内容本身就是一个数值类型的字符串,那么将来在转换的时候会返回自己. b) 如果转换的内容本身不是一个数值类型的字符串,那么在转换的时候结果是NaN. c) 如果要转换的内容是空的字符串,那以转换的结果是0. d) 如果是其它的字符,那么将来在转换的时候结果是NaN. 2)

  • js 数据类型转换总结笔记

    javascript有如下数据类型的转换方法: 一,转换成数字 xxx*1.0 转换成字符串 xxx+"" 二,从一个值中提取另一种类型的值,并完成转换工作. .提取字符串中的整数:parseInt(); 例:parseInt("123zhang")的结果为123 .提取字符串中的浮点数:parseFloat(); 例:parseFloat("0.55zhang")的结果为0.55 .执行用字符串表示的一段javascript代码:eval();

随机推荐