javascrtip的操作符和隐式转换,听说[] == ![]
Js中的操作符想必大家都不陌生了,除开常用的加减乘除,还有位操作符,一元操作符 ,三元操作符,这篇文章中,我们主要学习
加减乘除
和关系操作符
。
-、 *、 /、 % 操作符(减、乘、除、求模)
js在进行这几种运算时,会将参与运算的双方都转化为Number类型,然后进行运算,不同类型的值转换为Number类型的结果如何呢? 我们往下看。
基本数据类型的转换规则
- 如果是Boolean值,true和false分别被转换为1和0
- 如果是Number值,直接返回本身
- 如果是null,返回0
- 如果是undefined,返回NaN
- 如果是字符串,则遵循以下规则
- 如果字符串中只包含数字(包含正负号),则转换为十进制数值,如"011"被转换为数字11
- 如果字符串中包含有效浮点数值,转换为浮点数,如"-1.1"被转换为数字-1.1
- 如果字符串中包含有效的十六进制格式值,转换为对应的十进制数值,如"0xff"转换为数值255
- 空字符串被转换为0
- 如果不包含上述字符串,则转换为NaN
Number(5) // 5
Number("5") // 5
Number("a") // NaN
Number(true) // 1
Number(false) // 0
Number(undefined) // NaN
Number(null) // 0
对象的转换规则
如果Number
函数的参数为对象类型,valueOf
和toString
两个方法转换的规则为:首先调用对象valueOf
方法,若该方法返回了一个引用类型的值(非原始值)如{},[],则继续调用toString
方法。若valueOf
方法返回了一个原始值如:1,"a",true等,则不再调用toString
方法。
了解valueOf和toString方法:
var a = {};
a.valueOf() // {}
a.toString() // "[object Object]"
// 这两个方法都存在于a对象的构造函数也就是 Object的原型中
Object.toString === a.constructor.prototype.toString // ture
我们通过重写valueOf和toString方法来检验上述规则
// 1: 若valueOf返回一个引用类型的值。
var a = {
valueOf: function(){
console.log("valueOf")
return {}
},
toString: function(){
console.log("toString")
return "box"
}
};
a + 1
// valueOf
// toString
// "box1"
// 2: 若valueOf返回一个基本类型的值
var a = {
valueOf: function(){
console.log("valueOf")
return 1
},
toString: function(){
console.log("toString")
return "box"
}
};
a + 1
// valueOf
// 2
// 3: 若toString中返回了一个引用类型的值呢?
var a = {
valueOf: function(){
console.log("valueOf")
return []
},
toString: function(){
console.log("toString")
return {}
}
};
a + 1
// valueOf
// toString
// Uncaught TypeError: Cannot convert object to primitive value
// 无法将对象转换为原始值
valueOf默认返回当前对象原始值,toString方法用于将当前对象以字符串的形式返回,返回"[object ObjectName]",其中 ObjectName 是对象类型的名称,如"[object object]", "[object NodeList]"
注意对于RegExp, Math, Date等对象的toString方法返回值都不尽相同。 可以看到当我们在对象的toString方法中返回了一个对象时,计算发生了错误。
若使用String(a)进行显示类型转换则先执行toString方法。
数组的转换规则
数组做为对象的一种,同样拥有valueOf和toString方法,同样满足对象隐式转换规则,首先调用valueOf返回自身,然后调用toString方法返回结果。
var a = [];
a.valueOf === Array.prototype.valueOf //true
数组的toString方法是将数组的每个元素转换为字符串,并将它们依次连接起来,两个元素之间用英文逗号作为分隔符进行拼接。如果这个元素是数组,此规则同样应用在此元素上:
//这里再 [5] => "5"之间首先应该是[5]调用valueOf方法返回[5]的过程
//下方我们将省略此过程
[] => "" => Number("") => 0
[5] => "5" => Number("5") => 5
["8"] => "8" => Number("8") => 8
["a"] => "a" => Number("a") => NaN
[{}] => "{}" => Number("{}") => NaN
[undefined] => "" => Number("") => 0
[null] => "" => Number("") => 0
[[1]] => "1" => Number("1") => 1
[true] => "true" => Number("true") => NaN
[1,2] => "1,2" => Number("1,2") => NaN
[[1], [2]] => "1,2" => Number("1,2") => NaN
测试一下,自己在控制台上试试下方输出:
["2"] / "-1.3" = ?
null * undefined = ?
"0xf" - {valueOf(){return "-022"}} = ?
[[4]] % 2 = ?
+操作符
在上面我们说了 - * / %的运算时的转换规则,接下来我们来看+运算符的一些规则。
在js中,+运算符除了表示加法运算外,还可以用于字符串的连接,具体规则如下: 若 A + B;
- 首先将A和B都转换为原始值,对象的转换规则如上,调用valueOf和toString方法。
- 然后对比转换后的数据类型,若有一方为String类型,则将A,B都转换为String类型,然后执行 + 连接符,结束。
- 否则将 A,B都转换为Number类型,然后执行加法操作,结束。
//转换为原始类型后有一方为String类型
1 + "1" // "11"
true + "1" //"true1"
({} + 1) //"[object Object]1"
[] + 1 => "" + 1 // "1"
//否则
true + true // 2
1 + true // 2
1 + {valueOf:function(){return 1}} // 2
==、 >=、 <=、 !=、 >、<等比较操作符
[] == ![] ??
js的 == 号有隐式转换的作用,转换规则为将 == 号两边的值都转换为
Number
然后进行处理。
1 == "1" => 1 == 1 // true
1 == true => 1 == 1 // true
2 == true => 2 == 1 // false
[] == false => 0 == 0 // true
[1] == 1 => 1 == 1 // true
2 == {valueOf:function(){return 2}} // true
[] == ![] => 0 == 0 //true
[1,2] == "1,2" => "1,2" == "1,2" // true
// 解析 [] == ![]为true的过程
等号左侧:
1: [].valueOf() => []
2: [].toString() => ""
3: Number("") => 0
等号右侧:
4: ![] => !Boolean([]) => false
5: Number(false) => 0
6: 0 == 0 //成立
&&, ||, !,等符号属于逻辑运算符,逻辑运算符会将对比值隐式转换为布尔值。逻辑操作符的优先级高于
==
操作符,所以在这里首先执行!
操作
这里有个知识点, 在javascript中,只有 "", +0, -0, null, undefined, false, NaN转换成布尔值为假, 所以 ![] === false
一道有意思的题目:
var a = ?;
if(a == 1 && a == 2 && a == 3){
console.log(true)
}
// 问当a为何值时打印出true
// 通过上面我们学到的隐式转换规则解决
// 我们知道若a为对象,每次的 == 都会执行一次隐式类型转换进而对比结果
// 而每次隐式转换都会重新调用valueOf方法。
a = {
v: 1,
valueOf: function(){
return this.v++;
}
}
总结
- Js在
-、 *、 /、 %
运算中,会将参与运算的双方都转换为Number
然后计算 +操作符
会检查运算双方的类型,只要由一方为String
则作为连接符
的方式使用==
等比较操作符在比较之前,会将比较双方转换为Number
类型然后进行比较- 逻辑运费操作符
&&、||、!
的优先级高于比较操作符