相等操作符(==)的隐式转换

相等操作符(==)的隐式转换

使用相等操作符,当两个操作数类型不相等时,会在比较前尝试将其转换为相同类型。转换规则如下

  • 数字和字符串比较,字符串会转换成数字值(ToNumber
  • 其中一个操作数为布尔值,会将布尔值转换为数值,如果为true则转换为1,如果为false则转换为0
  • null == undefinedundefined == null返回true
  • 一个对象和数字或字符串比较,JavaScript会尝试返回对象的默认值。操作符会尝试通过valueOftoString将对象转换成原始值(ToPrimitive
  • 两个操作数均为对象,当它们的引用相同则返回true

NaN == NaN; //false +0 == -0; //true

原始数据 (原始值、原始数据类型)是一个非 object 类型并且没有自己的方法的数据。在 JavaScript 中,有六种原始数据类型:stringnumberbooleannullundefinedsymbol。除了nullundefined,所有原始值都有包裹这个原始值的等价对象,这个包裹对象的valueOf()方法返回原始值。

ToNumberToPrimitive

上述转换涉及到两个内部的方法ToNumberToPrimitive

ToNumber

ToNumber方法对不同类型返回结果如下

  • 布尔值,true–>1false–>+0
  • undefined–>NaN
  • null–>+0
  • 数字,数字对应值
  • 字符串,只包含有效数字格式字符串,转换为对应十进制数值(空字符转化为0),其他转换为NaN
    • 字符串只包含数字(包括前面正负号)字符串’123’–>123,’011’–>11(前导零忽略)
    • 字符串是有效的浮点数,转换为对应的浮点数值
    • 字符串是有效的十六进制’0x12’–>18
  • 对象,ToNumber(ToPrimitive(value, Number))

    http://www.ecma-international.org/ecma-262/5.1/#sec-9.3

ToPrimitive

ToPrimitive(input, [PreferredType])input转换成原始值,如果一个对象可以转换成多种原始值,可以通过PreferredType转换成指定类型。

ToPrimitive转换规则如下

  • input为原始值,则返回输入值
  • input为对象,则需要通过PreferredType来确定
PreferredType设置为String
  • 调用该对象toString方法,结果是原始值,则返回结果
  • 调用该对象valueOf方法,结果是原始值,则返回结果
  • 抛出TypeError异常
PreferredType设置为Number
  • 调用该对象valueOf方法,结果是原始值,则返回结果
  • 调用该对象toString方法,结果是原始值,则返回结果
  • 抛出TypeError异常

PreferredType没有设置值时,如果该对象为Date类型,行为与设置String一致,其他与设置Number一致。

http://www.ecma-international.org/ecma-262/5.1/#sec-9.1
http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.8

valueOftoString方法

Object.prototype包含valueOftoString这两个方法,所以继承至Object的对象都可以调用(如果对象重写了该方法,则调用自身)。

valueOf

JavaScript的内置对象都重写了valueOf方法,以实现更适合自身的功能需要。常见的返回值如下

  • 对象有原始值(number,string,boolean,symbol),返回原始值
  • Date对象,返回毫秒数(UTC)
  • 其他,返回对象本身
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let num = 123;
console.log(num.valueOf()); //123
let str = 'hello';
console.log(str.valueOf()); //'hello'
let bool = true;
console.log(bool.valueOf()); //true
let syb = Symbol('foo');
console.log(syb.valueOf()); //Symbol(foo)
let arr = ['abc', 1, 6];
console.log(arr.valueOf() === arr); //true
let foo = function(){};
console.log(foo.valueOf() === foo); //true
let reg = new RegExp(/.+/);
console.log(reg.valueOf() === reg); //true
let obj = {
name: '张三'
}
console.log(obj.valueOf() === obj); //true

toString

调用toString返回值如下

  • 该对象重写了toString方法,如NumberBooleanStringArrayDateFunctionRegExp
  • nullundefined通过call调用toStringObject.prototype.toString.call(null)),分别返回[object Null][object Undefined]
  • 否则继承自Object.prototype,返回 '[object type]',其中type是对象的类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
console.log(Number.prototype.hasOwnProperty('toString')); //true
console.log(Boolean.prototype.hasOwnProperty('toString')); //true
console.log(String.prototype.hasOwnProperty('toString')); //true
console.log(Symbol.prototype.hasOwnProperty('toString')); //true
console.log(Date.prototype.hasOwnProperty('toString')); //true
console.log(RegExp.prototype.hasOwnProperty('toString')); //true
console.log(Function.prototype.hasOwnProperty('toString')); //true
let num = 123;
console.log(num.toString()); //'123'
let bol = true;
console.log(bol.toString()); //'true'
let str = 'hello';
console.log(str.toString()); //'hello'
let syb = Symbol('foo');
console.log(syb.toString()); //'Symbol(foo)'
let date = new Date();
console.log(date.toString()); //'Sun Feb 25 2018 02:34:47 GMT-0800 (PST)' 返回一个美式英语日期格式的字符串
let foo = function(){};
console.log(foo.toString()); //'function (){}' 返回表示当前函数源代码的字符串,包括 function关键字,形参列表,大括号,以及函数体中的内容
let reg = new RegExp(/.+/);
console.log(reg.toString()); //'/.+/' 返回一个表示该正则表达式的字符串。
let arr = ['abc', 1, 6];
console.log(arr.toString()); //'abc,1,6' 返回值经调用 join() 方法连接(由逗号隔开)组成
let obj = {
name: '张三'
}
console.log(obj.toString()); //'[object Object]'

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/toString

示例

1
[] == !{}; //true
  • !优先级比==高,!{}结果为false
  • [] == false,其中一个操作数为布尔值,先将布尔值转为数值ToNumberfalse转为数值结果为0
  • [] == 0,对象与数值比较,将对象转换为原始值ToPrimitive(value, Number)Date类型ToPrimitive(value, String))。
  • 先调用valueOf方法,回返回[]本身,此时不是原始值,然后调用toString方法返回''
  • '' == 0字符串与数值比较,字符串转换为数值ToNumber,空字符转换为数值结果为0,所以最终表达式为0 == 0,返回true