玩命加载中 . . .

JS 之数据类型转换


显式数据类型转化

ES6 前,JavaScript 共有六种数据类型:UndefinedNullBooleanNumberStringObject

原始值转布尔

我们使用 Boolean函数 将类型转换成布尔类型,在 JavaScript 中,只有 6 种值可以被转换成 false,其他都会被转换成 true。

// 当 Boolean 函数不传任何参数时,会返回 false。 
console.log(Boolean()) // false
console.log(Boolean(false)) // false 
console.log(Boolean(undefined)) // false 
console.log(Boolean(null)) // false 
console.log(Boolean(+0)) // false 
console.log(Boolean(-0)) // false 
console.log(Boolean(NaN)) // false 
console.log(Boolean("")) // false

原始值转数字

我们可以使用 Number函数 将类型转换成数字类型。如果 Number 函数不传参数,返回 +0;如果有参数,隐式调用 ToNumber(value),ToNumber 则直接给了一个对应的结果表,表如下。但如果参数无法被转换为数字,则返回 NaN

参数类型 结果
Undefined NaN
Null +0
Boolean 如果参数是true,返回1;参数为false,返回+0
Number 返回与之相等的值
String 这段比较复杂,看例子
// 不传参
console.log(Number()) // +0 
// 传参
- Undefined
console.log(Number(undefined)) // NaN 
- Null
console.log(Number(null)) // +0 
- Boolean
console.log(Number(true)) // 1 
console.log(Number(false)) // +0 
- String
console.log(Number("123")) // 123 
console.log(Number("-123")) // -123 
console.log(Number("1.2")) // 1.2 
console.log(Number("000123")) // 123 
console.log(Number("-000123")) // -123 
console.log(Number("0x11")) // 17 
console.log(Number("")) // 0 
console.log(Number(" ")) // 0 
console.log(Number("123 123")) // NaN 
console.log(Number("foo")) // NaN 
console.log(Number("100a")) // NaN

如果通过 Number 转换函数传入一个字符串,它会试图将其转换成一个整数或浮点数,而且会忽略所有前导的 0;如果有一个字符不是数字,结果都会返回 NaN。鉴于这种严格的判断,我们一般还会使用更加灵活的 parseIntparseFloat 进行转换。

  • parseInt 只解析整数。如果字符串前缀是 “0x” 或者”0X”,会将其解释为十六进制数。
    console.log(parseInt("0xFF")) // 255 
    console.log(parseInt("0.1")) // 0
  • parseFloat 则可以解析整数和浮点数。
    console.log(parseFloat("3.14 abc")) // 3.14 
    console.log(parseFloat(".1")) // 0.1 
  • parseInt 和 parseFloat 都会跳过任意数量的前导空格,尽可能解析更多数值字符,并忽略后面的内容。如果第一个非空格字符是非法的数字直接量,将最终返回 NaN
    console.log(parseInt("3 abc")) // 3 
    console.log(parseInt("-12.34")) // -12 

    原始值转字符串

    我们使用 String函数 将类型转换成字符串类型。如果 String 函数不传参数,返回空字符串,如果**有参数,隐式调用 ToString(value)**。而 ToString 也给了一个对应的结果表,表如图:
    参数类型 结果
    Undefined “undefined”
    Null “null”
    Bolean 如果参数是true,返回true。参数为false,返回false
    Number 又是比较复杂,可以看例子
    String 返回与之相等的值
    // 不传参
    console.log(String()) // 空字符串 
    // 传参
    - Undefined
    console.log(String(undefined)) // undefined 
    - Null
    console.log(String(null)) // null 
    - Boolean
    console.log(String(true)) // true 
    console.log(String(false)) // false 
    - Number
    console.log(String(0)) // 0 
    console.log(String(-0)) // 0 
    console.log(String(NaN)) // NaN 
    console.log(String(Infinity)) // Infinity 
    console.log(String(-Infinity)) // -Infinity 
    console.log(String(1)) // 1

    原始值转对象

    原始值通过调用 String()Number() 或者 Boolean() 构造函数,转换为它们各自的包装对象,(nullundefined 属于例外,当将它们用在期望是一个对象的地方都会造成一个类型错误 (TypeError) 异常,而不会执行正常的转换)。
    var b = new Number(a);  
    console.log(typeof b); // object

    对象转布尔值

    所有对象(包括数组和函数)都转换为 true,对于包装对象也是这样。
    console.log(Boolean(new Boolean(false))) // true

    对象转字符串和数字

    对象到字符串 和 对象到数字 的转换都是通过调用待转换对象的一个方法来完成的。而 JavaScript 对象有两个不同的方法来执行转换,一个是 toString,一个是 valueOf。注意这个跟上面所说的 ToString 和 ToNumber 是不同的,这两个方法是真实暴露出来的方法。
  • 当调用对象的 toString 方法时,其实调用的是 Object.prototype 上的 toString 方法。
    • 然而 JavaScript 下的很多类根据各自的特点,定义了更多版本的 toString 方法。例如:
    • 数组的 toString 方法将每个数组元素转换成一个字符串,并在元素之间添加逗号后合并成结果字符串。
      console.log([].toString()) // "" 
      console.log([0].toString()) // 0
      console.log([1, 2, 3].toString()) // 1,2,3 
    • 函数的 toString 方法返回函数本身
      console.log(({}).toString()) // [object Object]
      console.log((function(){var a = 1;}).toString()) // function (){var a = 1;} 
    • 日期的 toString 方法返回一个可读的日期和时间字符串。
      var date = new Date(2010, 0, 1);
      console.log(date.toString()) // Fri Jan 01 2010 00:00:00 GMT+0800 (CST)
    • RegExp 的 toString 方法返回一个表示正则表达式直接量的字符串。
      console.log((/\d+/g).toString()) // /\d+/g
  • 而另一个转换对象的函数是 valueOf,表示对象的原始值。默认的 valueOf 方法返回这个对象本身,数组、函数、正则简单的继承了这个默认方法,也会返回对象本身。日期是一个例外,它会返回它的一个内容表示: 1970 年 1 月 1 日以来的毫秒数。
    var date = new Date(2017, 4, 21);
    console.log(date.valueOf()) // 1495296000000
  1. 对象转字符串

对象到字符串是如何转换的,其实就是 ToString 方法的对应表,只是这次我们加上 Object 的转换规则:

参数类型 结果
Object 1.primValue = ToPrimitive(input, String)
2.返回ToString(primValue)

所谓的 ToPrimitive 方法,其实就是输入一个值,然后返回一个一定是基本类型的值。

  • 语法:ToPrimitive(input[, PreferredType])
    • 第一个参数是 input,表示要处理的输入值。如果传入的 input 是 Undefined、Null、Boolean、Number、String 类型,直接返回该值。
    • 第二个参数是 PreferredType,非必填,表示希望转换成的类型,有两个值可以选,Number 或者 String。当不传入 PreferredType 时,如果 input 是日期类型,相当于传入 String,否则,都相当于传入 Number。
  • 如果是 ToPrimitive(obj, Number),处理步骤如下:

    • 如果 obj 为 基本类型,直接返回
    • 否则,调用 valueOf 方法,如果返回一个原始值,则 JavaScript 将其返回。
    • 否则,调用 toString 方法,如果返回一个原始值,则 JavaScript 将其返回。
    • 否则,JavaScript 抛出一个类型错误异常。
  • 如果是 ToPrimitive(obj, String),处理步骤如下:

    • 如果 obj为 基本类型,直接返回
    • 否则,调用 toString 方法,如果返回一个原始值,则 JavaScript 将其返回。
    • 否则,调用 valueOf 方法,如果返回一个原始值,则 JavaScript 将其返回。
    • 否则,JavaScript 抛出一个类型错误异常。

初次总结:当我们用 String 方法转化一个值的时候,如果是基本类型,就参照 “原始值转字符” 这一节的对应表,如果不是基本类型,我们会将调用一个 ToPrimitive 方法,将其转为基本类型,然后再参照“原始值转字符” 这一节的对应表进行转换。

最终总结:对象转字符串(就是 Number() 函数)可以概括为:

  • 如果对象具有 toString 方法,则调用这个方法。如果他返回一个原始值,JavaScript 将这个值转换为字符串,并返回这个字符串结果。
  • 如果对象没有 toString 方法,或者这个方法并不返回一个原始值,那么 JavaScript 会调用 valueOf 方法。如果存在这个方法,则 JavaScript 调用它。如果返回值是原始值,JavaScript 将这个值转换为字符串,并返回这个字符串的结果。
  • 否则,JavaScript 无法从 toString 或者 valueOf 获得一个原始值,这时它将抛出一个类型错误异常。
  1. 对象转数字

其实,从对象到数字的转换也是一样:
|参数类型|结果|
|:–:|:–|
|Object|1.primValue = ToPrimitive(input, Number)
2.返回ToNumber(primValue)|

虽然转换成基本值都会使用 ToPrimitive 方法,但传参有不同,最后的处理也有不同 -> 转字符串调用的是 ToString,转数字调用 ToNumber。

对象转数字的过程中,JavaScript 做了同样的事情,只是它会首先尝试 valueOf 方法。

  • 如果对象具有 valueOf 方法,且返回一个原始值,则 JavaScript 将这个原始值转换为数字并返回这个数字。
  • 否则,如果对象具有 toString 方法,且返回一个原始值,则 JavaScript 将其转换并返回。
  • 否则,JavaScript 抛出一个类型错误异常。
    // 例子
    console.log(Number({})) // NaN
    console.log(Number({a : 1})) // NaN
    console.log(Number([])) // 0
    console.log(Number([0])) // 0
    console.log(Number([1, 2, 3])) // NaN
    console.log(Number(function(){var a = 1;})) // NaN
    console.log(Number(/\d+/g)) // NaN
    console.log(Number(new Date(2010, 0, 1))) // 1262275200000
    console.log(Number(new Error('a'))) // NaN
    注意,在以上代码块中,[] 和 [0] 都返回了 0,而 [1, 2, 3] 却返回了一个 NaN。我们分析一下原因:
  • 当我们 Number([]) 的时候,先调用 [] 的 valueOf 方法,此时返回 [],因为返回了一个对象而不是原始值,所以又调用了 toString 方法,此时返回一个空字符串,接下来调用 ToNumber 这个规范上的方法,参照对应表,转换为 0, 所以最后的结果为 0。
  • 而当我们 Number([1, 2, 3]) 的时候,先调用 [1, 2, 3] 的 valueOf 方法,此时返回 [1, 2, 3],再调用 toString 方法,此时返回 1,2,3,接下来调用 ToNumber,参照对应表,因为无法转换为数字,所以最后的结果为 NaN。

隐式数据类型转化

一元操作符 +

当 + 运算符作为一元操作符的时候,会调用 ToNumber 处理该值,既然是调用 ToNumber 方法,当输入的值是对象的时候,先调用 ToPrimitive(input, Number) 方法,例子:

console.log(+[]);
    // [] 调用 valueOf 方法,返回一个空数组;
    // 因为不是原始值,继续调用 toString 方法,返回 "";
    // 得到返回值后,然后再调用 ToNumber 方法,"" 对应的返回值是 0,所以最终返回 0。
console.log(+['1']); // 1 
console.log(+['1', '2', '3']); // NaN 
console.log(+{}); // NaN

二元操作符 +

当计算 value1 + value2时:

lprim = ToPrimitive(value1)
rprim = ToPrimitive(value2)
  • 如果 lprim 是字符串或者 rprim 是字符串,那么返回 ToString(lprim) 和 ToString(rprim)的拼接结果
  • 返回 ToNumber(lprim) 和 ToNumber(rprim)的运算结果

1.Null 与数字

console.log(null + 1);
  • lprim = ToPrimitive(null) 因为null是基本类型,直接返回,所以 lprim = null
  • rprim = ToPrimitive(1) 因为 1 是基本类型,直接返回,所以 rprim = 1
  • lprim 和 rprim 都不是字符串
  • 返回 ToNumber(null) 和 ToNumber(1) 的运算结果:ToNumber(null) 的结果为0,ToNumber(1) 的结果为 1。所以,null + 1 相当于 0 + 1,最终的结果为数字 1。

2.数组与数组

console.log([] + []);
  • lprim = ToPrimitive([]),[]是数组,相当于ToPrimitive([], Number),先调用valueOf方法,返回对象本身,因为不是原始值,调用toString方法,返回空字符串””
  • rprim类似。
  • lprim和rprim都是字符串,执行拼接操作:[] + []相当于 “” + “”,最终的结果是空字符串””。

3.数组与对象

// 两者结果一致 
console.log([] + {});   
console.log({} + []);
  • lprim = ToPrimitive([]),lprim = “”
  • rprim = ToPrimitive({}),相当于调用 ToPrimitive({}, Number),先调用 valueOf 方法,返回对象本身,因为不是原始值,调用 toString 方法,返回 “[object Object]”
  • lprim 和 rprim 都是字符串,执行拼接操作:[] + {} 相当于 “” + “[object Object]”,最终的结果是 “[object Object]”。
    console.log(1 + true); // 2 
    console.log({} + {}); // "[object Object][object Object]" 
    console.log(new Date(2017, 04, 21) + 1) // "Sun May 21 2017 00:00:00 GMT+0800 (CST)1"

== 相等

  1. null和undefined

    console.log(null == undefined);
        规则2)x是null并且y是undefined,返回true
        规则3)x是undefined并且y是null,返回true
        // 所以例子的结果自然为 true。
  2. 字符串与数字

    console.log('1' == 1);
        规则4)x是数字,y是字符串,判断x == ToNumber(y)
        规则5)x是字符串,y是数字,判断ToNumber(x) == y
        // 都先转换成数字后再进行比较,所以例子的结果为true。
  3. 布尔值和其他类型

    console.log(true == '2')
        规则6)x是布尔值,判断ToNumber(x) == y
        规则7)y是布尔值,判断x ==ToNumber(y)
        // 当一方出现布尔值的时候,就会对这一方的值进行ToNumber处理,也就是说true会被转化成1,true == '2' 就相当于 1 == '2' 就相当于 1 == 2,结果自然是 false。
            // 所以当一方是布尔值的时候,会对布尔值进行转换,因为这种特性,所以尽量少使用 xx == true 和 xx == false 的写法。
  4. 对象与非对象

    console.log( 42 == ['42'])
        规则8)x是字符串或者数字,y是对象,判断x == ToPrimitive(y)
        规则9)x是对象,y不是字符串或者数字,判断ToPrimitive(x) == y
        // 以这个例子为例,会使用 ToPrimitive 处理 ['42'],调用valueOf,返回对象本身,再调用 toString,返回 '42',所以42 == ['42'] 相当于 42 == '42' 相当于42 == 42,结果为 true。
  5. 其他:一概返回 false
    再多举几个例子进行分析:

    console.log(false == undefined)
        // false == undefined 相当于 0 == undefined 不符合上面的情形,执行最后一步 返回 false
    
    console.log(false == [])
        // false == [] 相当于 0 == [] 相当于 0 == '' 相当于 0 == 0,结果返回 true
    
    console.log([] == ![])
        // 首先会执行 ![] 操作,转换成 false,相当于 [] == false 相当于 [] == 0 相当于 '' == 0 相当于 0 == 0,结果返回 true

最后再举一些会让人踩坑的例子:

console.log(false == "0")
console.log(false == 0)
console.log(false == "")

console.log("" == 0)
console.log("" == [])

console.log([] == 0)

console.log("" == [null])
console.log(0 == "\n")
console.log([] == 0)

// 以上均返回 true

常见的类型转换

类型 to Boolean to Number to String
Boolean true true 1 “true”
Boolean false false 0 “false”
Number 123 true 123 “123”
Number Infinity true Infinity “Infinity”
Number 0 false 0 “0”
Number NaN false NaN “NaN”
String “” false 0 “”
String “123” true 123 “123”
String “123abc” true NaN “123abc”
String “abc” true NaN “abc”
Null null false 0 “null”
Undefined undefined false NaN “undefined”
Function function() {} true NaN
Object {} true NaN “[object Object]”
Array [] true 0 “”
Array [“abc”] true NaN “abc”
Array [“123”] true 123 “123”
Array [“123”, “abc”] true NaN “123, abc”

文章作者: hcyety
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hcyety !
评论
  目录