Skip to content
大纲

数据类型隐式转换规律

  1. 原始基础类型:Undefined、 Null、 String、 Number、 Boolean、 Symbol、Object、bigInt
  2. JS 内置对象:Date, Array, Math, Number, Boolean, String, Array, RegExp, Function, Error。

隐式中涉及的三种转换

  1. 对象转换为原始值 ,ToPrimitive(input, PreferredType)

    1. PreferredType 被标记为Number

      1. 如果输入值是原始值 ,则直接返回
      2. 否则如果输入的值是一个对象,则调用对象的 valueOf() 方法;如果 valueof()返回的是一个原始值,则返回这个原始值。
      3. 否则调用对象toString()方法,如果返回的是一个原始值,则返回原始值;否则抛出 TypeError 异常
    2. PreferredType 被标记为String

      1. 如果输入值是原始值 ,则直接返回
      2. 否则调用对象的toString()方法;如果 toString()返回的是一个原始值,则返回这个原始值。
      3. 否则输入的值是一个对象,则调用该对象的valueOf(),如果返回的是一个原始值,则返回原始值;否则抛出 TypeError 异常
    3. 无参数 PreferredType 时

      1. 该对象为 Date 类型,则 PreferredType 被设置为String
      2. 否则 PreferredType 被设置为Number
  • 值转换为数字即 ToNumber()
参数结果
undefinedNaN
null+0
布尔值true 转换 1,false 转换为+0
数字无须转换
字符串有字符串解析为数字,例如:‘324’转换为 324,‘qwer’转换为 NaN
对象(obj)先进行 ToPrimitive(obj, Number)转换得到原始值,在进行ToNumber转换为数字
  • 值转换为字符串即 ToString()
参数结果
undefined'undefined'
null'null'
布尔值转换为'true' 或 'false'
数字数字转换字符串,比如:1.765 转为'1.765'
字符串无须转换
对象(obj)先进行 ToPrimitive(obj, String)转换得到原始值,在进行ToString转换为字符串
  • valueOf() 与 toString()是 Object.prototype 上的属性,故所有对象都拥有。
  • Number, Boolean, String 这三种构造函数生成基础值的对象形式
javascript
let num = new Number('123')
num.valueOf() // 123
var str = new String('12df')
str.valueOf() // '12df'

var bool = new Boolean('fd')
bool.valueOf() // true
  • Date 特殊对象
javascript
var a = new Date()
a.valueOf() // 1515143895500
  • 除此之外返回的都是 this,即对象本身
javascript
var a = new Array()
a.valueOf() === a // true

var b = new Object({})
b.valueOf() === b // true
  • toString()方法
    • Number、Boolean、String、Array、Date、RegExp、Function 这几种构造函数生成的对象,通过 toString 转换后会变成相应的字符串的形式,因为这些构造函数上封装了自己的 toString 方法。如:
javascript
Number.prototype.hasOwnProperty('toString') // true
Boolean.prototype.hasOwnProperty('toString') // true
String.prototype.hasOwnProperty('toString') // true
Array.prototype.hasOwnProperty('toString') // true
Date.prototype.hasOwnProperty('toString') // true
RegExp.prototype.hasOwnProperty('toString') // true
Function.prototype.hasOwnProperty('toString') // true

var num = new Number('123sd')
num.toString() // 'NaN'

var str = new String('12df')
str.toString() // '12df'

var bool = new Boolean('fd')
bool.toString() // 'true'

var arr = new Array(1, 2)
arr.toString() // '1,2'

var d = new Date()
d.toString() // "Wed Oct 11 2017 08:00:00 GMT+0800 (中国标准时间)"

var func = function () {}
func.toString() // "function () {}"
  • 除这些对象及其实例化对象之外,其他对象返回的都是该对象的类型,(有问题欢迎告知),都是继承的 Object.prototype.toString 方法。
javascript
var obj = new Object({})
obj.toString() // "[object Object]"

Math.toString() // "[object Math]"

WARNING

故:从 valueOf()与 toString()函数对对象的转换可知,对于 ToPrimitive(input, preferredType),preferredType没有设定时,除Date被设置为String类型外,其他会被设置为 Number;

因为valueOf将 Number、String、Boolean基础类型的对象类型转换为基础类型,Data转换为毫秒数,其他返回为对象本身;而toString会将所有对象转换为字符串,显然 valueOf 转换更合理些

因此先将PreferredType设置为Number类型,进行 valueOf 转换;而 Date 类型的对象,没有设定 PreferredType 时,默认设置为String

算术运算中的隐私转换

  1. ({} + {}) = ?

    1. 两个对象的值进行+运算符,肯定要先进行隐式转换为原始类型才能进行计算。
    2. 进行 ToPrimitive 转换,由于没有指定PreferredType类型,{}会使默认值为 Number,进行 ToPrimitive(input, Number)运算。
    3. 所以会执行valueOf方法,({}).valueOf(),返回的还是{}对象,不是原始值。
    4. 继续执行toString方法,({}).toString(),返回"[object Object]",是原始值。 故得到最终的结果,"[object Object]" + "[object Object]" = "[object Object][object Object]"
  2. 2 * {} = ?

    1. 首先*运算符只能对 number 类型进行运算,故第一步就是对{}进行ToNumber类型转换。
    2. 由于{}是对象类型,故先进行原始类型转换,ToPrimitive(input, Number)运算。
    3. 所以会执行valueOf方法,({}).valueOf(),返回的还是{}对象,不是原始值。
    4. 继续执行 toString 方法,({}).toString(),返回"[object Object]",是原始值。
    5. 转换为原始值后再进行 ToNumber 运算,"[object Object]"就转换为 NaN。 故最终的结果为 2 * NaN = NaN

比较运算隐式转换---不同类型的简单类型比较时,先向 Number 转,再向 String 类型

  1. 比较运算 x==y, 其中 x 和 y 是值,返回 true 或者 false。这样的比较按如下方式进行:
    1. 若 Type(x) 与 Type(y) 类型相同, 则
      1. 若 Type(x) 与 Type(y)为 Undefined, 返回 true。
      2. 若 Type(x) 与 Type(y)为 Null, 返回 true。
      3. 若 Type(x) 与 Type(y)为 Number, 则
        1. 若 x 为 NaN, 返回 false。
        2. 若 y 为 NaN, 返回 false。
        3. 若 x 与 y 为相等数值, 返回 true。
        4. 若 x 为 +0 且 y 为 −0, 返回 true。
        5. 若 x 为 −0 且 y 为 +0, 返回 true。
        6. 返回 false。
      4. 若 Type(x) 与 Type(y) 为 String, 则当 x 和 y 为完全相同的字符序列(长度相等且相同字符在相同位置)时返回 true。 否则, 返回 false。
      5. 若 Type(x) 与 Type(y) 为 Boolean, 当 x 和 y 为同为 true 或者同为 false 时返回 true。 否则, 返回 false。
      6. 当 x 和 y 为引用同一对象时返回 true。否则,返回 false。
    2. 若 x 为 null 且 y 为 undefined, 返回 true。
    3. 若 x 为 undefined 且 y 为 null, 返回 true。
    4. 若 Type(x) 为 Number 且 Type(y) 为 String,返回比较 x == ToNumber(y) 的结果。
    5. 若 Type(x) 为 String 且 Type(y) 为 Number,返回比较 ToNumber(x) == y 的结果。
    6. 若 Type(x) 为 Boolean, 返回比较 ToNumber(x) == y 的结果。
    7. 若 Type(y) 为 Boolean, 返回比较 x == ToNumber(y) 的结果。
    8. 若 Type(x) 为 String 或 Number,且 Type(y) 为 Object,返回比较 x == ToPrimitive(y) 的结果。
    9. 若 Type(x) 为 Object 且 Type(y) 为 String 或 Number, 返回比较 ToPrimitive(x) == y 的结果。
    10. 其他情况返回 false。

对象类型转换步骤

  • Object 与 Number,把 Object.valueOf() 和 Object.toString()转换为基本数字类型
  • 数组类型转换,通过 toString()-->Array.join()等于 shift 操作 取出第一个元素

TIP

  • x、y 类型相同时:

    1. Number 类型时,NaN 与 NaN 不相待
    2. 对象时,需要是引用同一个对象
  • 和类型不相同时:

    1. x,y 为 null、undefined 两者中一个则相等
    2. x、y 为 Number 和 String 类型时,则转换为 Number 类型比较。
    3. 有 Boolean 类型时,Boolean 转化为 Number 类型比较。
    4. 一个 Object 类型,一个 String 或 Number 类型,将 Object 类型进行原始转换后,按上面流程进行原始值比较。 :::

重写 valueOf 与 toString 方法的例子

javascript
var a = {
  valueOf: function () {
    return 1
  },
  toString: function () {
    return '123'
  }
}
true == a // true;

分析下面表达式的运算过程

js
;[] == !{}
  1. ! 运算符优先级高于==,故先进行!运算。
  2. !{}运算结果为 false,结果变成 [] == false 比较。
  3. 等式右边 y = ToNumber(false) = 0。结果变成 [] == 0。
  4. 比较变成 ToPrimitive([]) == 0。
    1. 按照上面规则进行原始值转换,[]会先调用 valueOf(),返回 this。
    2. valueOf()的结果不是原始值,继续调用 toString 方法,x = [].toString() = ''。
  5. 故结果为 '' == 0 比较。
  6. 等式左边 x = ToNumber('') = 0。
  7. 所以结果变为: 0 == 0,返回 true,比较结束。

使 a == 1 && a == 2 && a == 3 成立的方案

javascript
// 解一
var a = {
  i: 1,
  toString: function () {
    return a.i++
  }
}
// 解二
var a = [1, 2, 3]
// 解三
var val = 0
Object.defineProperty(window, 'a', {
  // 这里要window,这样的话下面才能直接使用a变量去 ==
  get: function () {
    return ++val
  }
})
console.log(a == 1 && a == 2 && a == 3)
javascript
// 试分析该段代码
const a = {
  i: 1,
  toString: function () {
    return a.i++
  }
}
if (a == 1 && a == 2 && a == 3) {
  console.log('hello world!')
}