数据类型隐式转换规律
- 原始基础类型:Undefined、Null、String、Number、Boolean、Symbol、Object、bigInt
- JS 内置对象:Date, Array, Math, Number, Boolean, String, Array, RegExp, Function, Error。
隐式中涉及的三种转换
对象转换为原始值,ToPrimitive(input, PreferredType)
PreferredType 被标记为
Number
- 如果输入值是原始值,则直接返回
- 否则如果输入的值是一个对象,则调用对象的
valueOf()
方法;如果 valueof() 返回的是一个原始值,则返回这个原始值。 - 否则调用对象
toString()
方法,如果返回的是一个原始值,则返回原始值;否则抛出 TypeError 异常
PreferredType 被标记为
String
- 如果输入值是原始值,则直接返回
- 否则调用对象的
toString()
方法;如果 toString() 返回的是一个原始值,则返回这个原始值。 - 否则输入的值是一个对象,则调用该对象的
valueOf()
,如果返回的是一个原始值,则返回原始值;否则抛出 TypeError 异常
无参数 PreferredType 时
- 该对象为 Date 类型,则 PreferredType 被设置为
String
- 否则 PreferredType 被设置为
Number
- 该对象为 Date 类型,则 PreferredType 被设置为
- 值转换为数字即 ToNumber()
参数 | 结果 |
---|---|
undefined | NaN |
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
算术运算中的隐私转换
({} + {}) = ?
- 两个对象的值进行 + 运算符,肯定要先进行隐式转换为原始类型才能进行计算。
- 进行 ToPrimitive 转换,由于没有指定
PreferredType
类型,{}会使默认值为 Number,进行 ToPrimitive(input, Number) 运算。 - 所以会执行
valueOf方法
,({}).valueOf(),返回的还是{}对象,不是原始值。 - 继续执行
toString方法
,({}).toString(),返回"[object Object]",是原始值。 故得到最终的结果,"[object Object]" + "[object Object]" = "[object Object][object Object]"
2 * {} = ?
- 首先*运算符只能对 number 类型进行运算,故第一步就是对{}进行
ToNumber
类型转换。 - 由于{}是对象类型,故先进行原始类型转换,ToPrimitive(input, Number) 运算。
- 所以会执行
valueOf
方法,({}).valueOf(),返回的还是{}对象,不是原始值。 - 继续执行 toString 方法,({}).toString(),返回"[object Object]",是原始值。
- 转换为原始值后再进行 ToNumber 运算,"[object Object]"就转换为 NaN。 故最终的结果为
2 * NaN = NaN
- 首先*运算符只能对 number 类型进行运算,故第一步就是对{}进行
比较运算隐式转换---不同类型的简单类型比较时,先向 Number 转,再向 String 类型
- 比较运算 x==y, 其中 x 和 y 是值,返回 true 或者 false。这样的比较按如下方式进行:
- 若 Type(x) 与 Type(y) 类型相同,则
- 若 Type(x) 与 Type(y) 为 Undefined,返回 true。
- 若 Type(x) 与 Type(y) 为 Null,返回 true。
- 若 Type(x) 与 Type(y) 为 Number,则
- 若 x 为 NaN,返回 false。
- 若 y 为 NaN,返回 false。
- 若 x 与 y 为相等数值,返回 true。
- 若 x 为 +0 且 y 为 −0,返回 true。
- 若 x 为 −0 且 y 为 +0,返回 true。
- 返回 false。
- 若 Type(x) 与 Type(y) 为 String, 则当 x 和 y 为完全相同的字符序列(长度相等且相同字符在相同位置)时返回 true。否则,返回 false。
- 若 Type(x) 与 Type(y) 为 Boolean, 当 x 和 y 为同为 true 或者同为 false 时返回 true。否则,返回 false。
- 当 x 和 y 为引用同一对象时返回 true。否则,返回 false。
- 若 x 为 null 且 y 为 undefined,返回 true。
- 若 x 为 undefined 且 y 为 null,返回 true。
- 若 Type(x) 为
Number
且 Type(y) 为String
,返回比较 x == ToNumber(y) 的结果。 - 若 Type(x) 为
String
且 Type(y) 为Number
,返回比较 ToNumber(x) == y 的结果。 - 若 Type(x) 为
Boolean
,返回比较 ToNumber(x) == y 的结果。 - 若 Type(y) 为
Boolean
,返回比较 x == ToNumber(y) 的结果。 - 若 Type(x) 为
String 或 Number
,且 Type(y) 为Object
,返回比较 x == ToPrimitive(y) 的结果。 - 若 Type(x) 为
Object
且 Type(y) 为String 或 Number
,返回比较 ToPrimitive(x) == y 的结果。 - 其他情况返回 false。
- 若 Type(x) 与 Type(y) 类型相同,则
对象类型转换步骤
- Object 与 Number,把 Object.valueOf() 和 Object.toString() 转换为基本数字类型
- 数组类型转换,通过 toString()-->Array.join() 等于 shift 操作 取出第一个元素
TIP
x、y 类型相同时:
- Number 类型时,NaN 与 NaN 不相待
- 对象时,需要是引用同一个对象
和类型不相同时:
- x,y 为 null、undefined 两者中一个则相等
- x、y 为 Number 和 String 类型时,则转换为 Number 类型比较。
- 有 Boolean 类型时,Boolean 转化为 Number 类型比较。
- 一个 Object 类型,一个 String 或 Number 类型,将 Object 类型进行原始转换后,按上面流程进行原始值比较。 :::
重写 valueOf 与 toString 方法的例子
javascript
var a = {
valueOf: function () {
return 1
},
toString: function () {
return '123'
}
}
true == a // true;
分析下面表达式的运算过程
js
;[] == !{}
- ! 运算符优先级高于==,故先进行!运算。
- !{}运算结果为 false,结果变成 [] == false 比较。
- 等式右边 y = ToNumber(false) = 0。结果变成 [] == 0。
- 比较变成 ToPrimitive([]) == 0。
- 按照上面规则进行原始值转换,[]会先调用
valueOf()
,返回 this。 - valueOf() 的结果不是原始值,继续调用 toString 方法,x = [].toString() = ''。
- 按照上面规则进行原始值转换,[]会先调用
- 故结果为 '' == 0 比较。
- 等式左边 x = ToNumber('') = 0。
- 所以结果变为: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!')
}