JS 类型转换,小尝一口

149 阅读5分钟

JavaScript 类型转换

字符串 数字 布尔值
undefined "undefined" NaN false
null "null" 0 false
true "true" 1
false "false" 0
"" 0 false
"1.2" 1.2 true
"one" NaN true
0 "0" false
-0 "0" false
NaN "NaN" false
Infinity "Infinity" true
-Infinity "-Infinity" true
1 "1" true
{} "[object Object]" NaN true
[] "" 0 true
[9] "9" 9 true
[1, 2, 3] "1,2,3" NaN true
['a'] "a" NaN true
function foo(){} "function foo(){}" NaN true

类型转换的基本规则

  • ToString
  • ToNumber
  • ToBoolean
  • ToPrimitive

ToString

负责非字符串字符串的强制类型转换。

基本数据类型
  • null => "null"
  • undefined => "undefined"
  • true => "true"
  • 12 => "12"
对象

除非自行定义,否则 toString() 返回内部属性 [[Class]] 的值。

这个属性无法直接访问,一般通过 Object.prototype.toString(..) 查看

如果对象有自己的 toString() 方法,字符串化时就会调用该方法并使用其返回值。

var a = {
    name: 'ym'
}
var b = {
    name: 'ym',
    toString: function() {
        return this.name;
    }
}

a.toString(); // "[object Object]"
b.toString(); // "ym"
数组

数组的默认 toString() 方法经过重新定义

var a = [1, 2, 3];
a.toString(); // "1,2,3"

ToNumber

基本数据类型
  • true => 1 false => 0
  • undefined => NaN
  • null => 0
对象(包括数组)

首先会被转换为相应的基本类型值。如果是非数字的基本类型值,则按上面的规则强制转换。

将值转换为相应基本类型的步骤:(ToPrimitive)

  1. 检查该值是否有 valueOf() 方法。
    • 如果有并且返回基本类型值,就使用该值进行强制类型转换。
    • 如果没有就使用 toString() 的返回值进行强制转换。
  2. 如果 valueOf()toString() 均不返回基本类型值,会产生 TypeError 错误。
// case1: 对象只有 valueOf 方法,并且返回基本类型值
// 此时就调用 valueOf
var a = {
    valueOf: function() {
        return '42';
    }
}

// case2: 对象只有 toString 方法,并且返回基本类型值
// 此时调用 toString
var b = {
    toString: function() {
        return '32';
    }
}

// case3:两种方法都有。并且 valueOf 返回基本类型值
// 此时调用 valueOf
var c = {
    valueOf: function() {
        return '42';
    },
    toString: function() {
        return '32'
    }
}

// case4: 两种方法都有。但是 valueOf 返回的不是基本类型值,而 toString 返回基本类型值
// 此时调用 toString
var d = {
    valueOf: function() {
        return [1, 2];
    },
    toString: function() {
        return '32'
    }
}

// case5: 两种方法都有。但是返回的都不是基本类型值
// 此时报错 Uncaught TypeError: Cannot convert object to primitive value
var e = {
    valueOf: function() {
        return [1, 2];
    },
    toString: function() {
        return [1, 3];
    }
}

Number(a); // 42
Number(b); // 32
Number(c); // 42
Number(d); // 32
Number(e); // Uncaught TypeError: Cannot convert object to primitive value

栗子:chestnut:

// [].valueOf() => []
// [].toString() => ""
// "" => 0
Number([]); // 0

// ['abc'].valueOf() => ['abc']
// ['abc'].toString() => "abc"
// "abc" => NaN
Number(['abc']); // NaN

ToBoolean

两类值
  • 可以被强制转换为 false 的值(注意这一部分的值比较少)

    • undefined
    • null
    • false
    • +0 -0 NaN
    • ""

    注意1[]{} 强制转换并不是 false 而是 true

    注意2

    var a = new Boolean(false);
    var b = new Number(0);
    var c = new String("");
    
    Boolean(a && b && c) // 三者强制转换均为 true,因为本质是对象
    
  • 其他(转换为 true

== 号的比较

两个值的类型相同

这种情况就只比较值是否相等,不会做类型转换。

正常情况
1 == 1 // true
'abc' == 'abc' // true
true == true // true
null == null // true
undefined == undefined // true
[] == [] // false
{} == {} //false
特别情况
NaN == NaN // false
+0 == -0 // true

两个值的类型不同

这种情况会发生隐式类型转换。会将其中之一或者二者都转换为相同的类型后再进行比较。

数字和字符串

不论二者的位置关系是怎么样,就是将字符串转换为数字类型

  • a 是数字,b 是字符串,返回 a == ToNumber(b)
  • a 是字符串,b 是数字,返回 ToNumber(a) == b
// case1: 数字 == 字符串
42 == '42' // true
42 === '42' // false

// case2: 字符串 == 数字
'42' == 42 // true

布尔和其他类型

  1. 布尔类型首先会转换成数字类型。(同样跟位置没有关系)

    => 因此问题变成了数字和其他类型的比较了

// case1:布尔 == 字符串
// 第一步:true => 1
// 第二步:'42' => 42
// 第三步:1 == 42 => false
true == '42' // false
false == '42' // false

// case2:布尔 == 数字
true == 1 // true
false == 0 // true

null 和 undefined

不论位置,结果为 true。

null == undefined // true
undefined == null //true

null 和 undefined 与其他类型

均返回 false。

null == false // false
undefined == false // false
null == "" // false
undefined == "" // false
null == 0 // false
undefined == 0 // false

对象和非对象

对象首先要转化为基本数据类型

  • a 是对象,b 是非对象,返回 a == ToPrimitive(b)
  • a 是非对象,b 是对象,返回 ToPrimitive(a) == b
// case1
// 第一步:[42] => '42'
// 第二步:'42' => 42
// 第三步:42 == 42 => true
42 == [42] // true

// case2:布尔和数组
// false => 0
// [] => ""
// "" => 0
[] == false // true
// ![] => false
![] == false // true
'abc' == new String('abc') // true
123 == new Number(123) // true
false == new Boolean(false) // true

null == Object(null) // false
undefined == Object(undefined) // false
NaN == new Number(NaN) // false

数组与其他

数组会转换为字符串,因此问题就变成了字符串与其他的比较

[] == false // true
[] == "" // true
[] == 0 // true

// ToBoolean(![]) => false
// [] == false => true
[] == ![] // true

// 因为先进行 !运算,所以 false 不会首先转换为数字
// 第一步:![] => false
// 第二步:false == false => true
![] == false // true

注意!:由于 !的优先级要高于 ==,因此会先进行 !运算,再进入 == 流程

+ 号的隐式转换

数字和字符串运算

两种情况
  • 字符串 + 数字 => 字符串
  • 数字 + 字符串 => 字符串
总结

字符串和数字存在加法运算,最终得到字符串。

加号和字符串

  • + 数字字符串 => 数字
  • + 非数字字符串 => NaN

参考

  • 《你不知道的JS》

  • 《JavaScript 权威指南》