WdBly Blog

懂事、有趣、保持理智

WdBly Blog

懂事、有趣、保持理智

周维 | Jim

603927378@qq.com

javascrtip的操作符和隐式转换规则

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类型然后进行比较
  • 逻辑运费操作符&&、||、!的优先级高于比较操作符