前端面试必须懂之Js操作符

1,290 阅读6分钟

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函数的参数为对象类型,valueOftoString两个方法转换的规则为:首先调用对象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类型然后进行比较
  • 逻辑运费操作符&&、||、!的优先级高于比较操作符