也许你们会以为js强制类型转换与操做符知道个大概就够了。是的,对于搬砖工来讲,够了。可是做为一个有追求的前端,我深信只有掌握了别人不肯意掌握的东西,才能让本身变得更强大更有竞争力。
或许你们不喜欢隐式类型转换,以为这东西太没人性。可是你有没有想过,这也许正是js语言的独特之处?我认同kyle大佬说的,若是你完全掌握了隐式类型转换,那么对你来讲,它就是“显式”类型转换了。
之因此先讲类型转换,是由于在操做符运算中涉及了大量的隐式类型转换。javascript
抽象操做ToPrimitive用于将引用类型转为原始类型。实现细节比较复杂,有兴趣的童鞋能够参考这里。前端
//模拟一个对象的转基本类型操做 ToPrimitive var o = {}; o[Symbol.toPrimitive] = function(hint) { console.log(hint) //hint字符串至为 string number default 中的一个 if (hint == "default" || hint == "number") { if (o.valueOf && typeof(o.valueof()) != 'object') { return o.valueOf() } else { return o.toString() } } else { if (o.toString && typeof(o.toString()) != 'object') { return o.toString() } else { return o.valueOf() } } } String(o) // string Number(o) // number 1+o // default 1-o // number o++ // number ++o // number
规则以下:java
string
(目前只有调用String()函数是执行这个顺序):首先检查该值是否有toString()方法。若是有而且返回基本类型值,就使用该值进行强制类型转换。若是没有就检查该值是否有valueOf()方法。若是有而且返回基本类型值就使用该回值来进行强制类型转换,若是没有或者返回的不是基本类型值,就抛出错误。number/default
(常见强制类型转换都是这个顺序):首先检查该值是否有valueOf()方法。若是有而且返回基本类型值,就使用该值进行强制类型转换。若是没有就检查该值是否有toString()方法。若是有而且返回基本类型值就使用该回值来进行强制类型转换,若是没有或者返回的不是基本类型值,就抛出错误。抽象操做 ToString,负责处理非字符串到字符串的强制类型转换。当须要一个值的字符串形式,就会进行 ToString 类型转换。segmentfault
String()函数就会执行抽象操做 ToString,遵循下列转换规则:数组
String() // '' String(0) // '0' String(true) // 'true' String(null) // 'null' String(undefined) // 'undefined' String(Symbol('asdf')) // "Symbol('asdf')" String({}) // '[Object object]' // 数组的默认 toString() 方法通过了从新定义,将全部单元字符串化之后再用 "," 链接起来 String([]) // '' String([1,2,3,4,'asdf']) // '1,2,3,4,asdf'
抽象操做 ToNumber,负责处理非数字到数字的强制类型转换。函数
Number()执行抽象操做 ToNumber,函数的转换规则以下。学习
Number() // 0 Number('') // 0 Number(' ') // 0 Number('0') // 0 Number('asdf') // NaN Number(true) // 1 Number(false) // 0 Number(null) // 0 Number(undefined) // NaN 与null不一样,须要注意 // 对象会先经过抽象操做ToPrimitive转为基本类型,而后再转数字 Number({}) // NaN Number([]) // 0 Number(['']) // 0 Number([' ']) // 0 Number(['0']) // 0 Number([1,2]) // NaN
抽象操做 ToBoolean,负责处理非布尔值到布尔值的强制类型转换。编码
转换为 boolean 类型是最为简单的一个。转换规则以下:code
(1) 能够被强制类型转换为 false
的值对象
(2) 其余值会被被强制类型转换为 true
这里有一个概念须要先理解:js的操做符和操做数组成了表达式,表达式一定会返回一个值。不管是一元操做++a
,仍是布尔操做[] || false
,都会返回一个值。另外关于js运算符优先级请参阅MDN的: 运算符优先级。
// 假设存在变量a +a // 一元加操做符 -a // 一元减操做符 ++a // 前置递增操做符 --a // 前置递减操做符 a++ // 后置递增操做符 a-- // 后置递减操做符
一元操做符指的是只能操做一个值的操做符,区别与加性操做符能够操做两个值(如a+b
)。
一元加操做符+
用于非数字的强制类型转换,做用等同于Number()
。如:
+'1.1' // 1.1 +'asdf' // NaN +true // 1 +false // 0 +null // 0 +undefined // NaN +{} // NaN +[] // 0 +new Date() // 1556258367546
一元减操做符-
行为与+
相似,只不过最终运算结果是负数。如-true
结果是-1
。
不一样于一元加减操做符,递增递减操做符只能做用于number
类型。若用于其余类型会直接抛错。
//前置递增 var a = 57; var b = ++a; console.log(b); // 58 //后置递增 var a = 57; var b = a++; console.log(b); // 57
前置递增和后置递增的区别在于,前置递增++a
的返回值是增长1
的,然后置递增a++
的返回值是不增长的。
递减和递增规则同样,再也不废话。
+
+
操做符经常使用于数学的计算和字符串的拼接,规则以下:
1+1 // 2 NaN+1 // NaN 'asdf'+'ghjk' // 'asdfghjk' 1+1+'1' // '21' []+1 // 1 null+undefined // 'nullundefined' []+{}+1 // '[Object object]1' 实际执行:''+'[Object object]'+1 {}+[]+1 // 1 {}位于行首会被解释为代码块,此处代码块被忽略,所以实际执行:+[]+1,结果为数字1
-
1-1 // 0 NaN-1 // NaN 10-true-null // 9 10-true-undefined // NaN []-1 // 0 ['11']-11 // 0 11-{} // NaN
乘性操做符包括乘法*
、除法/
、除余(求模)%
。规则以下:
数值计算较为特殊的以下:
Infinity*0 // NaN Infinity/Infinity // NaN 0/0 // NaN Infinity%a // NaN a为任意数值 a%0 // NaN a为任意数值
!
逻辑非操做符会将任意值转换为一个布尔值,转换规则和Boolean()
函数相反。连续使用两个逻辑非操做符,等同于调用了Boolean()
。常见有大牛写代码用!!isTrue
来代替Boolean(isTrue)
函数。
!undefined // true !!undefined // false !NaN // true !!NaN // false !1234 // false !!1234 // true !'' // true !!'' // false
||
短路操做:若是第一个操做数可以决定结果,那么就不会再对第二个操做数求值。
逻辑或操做符是短路操做,若是第一个操做数的求值结果(布尔求值,下同)为true
,则直接返回第一个操做数,再也不对第二个操做数求值。若是第一个操做符求职结果为false
,则返回第二个操做数。所以,常见大神写代码isExist || getIsExist()
,就是利用的短路操做,若是isExist
求值结果为true,就再也不执行getExist()
函数。
[] || 0 // [] 对象(包括数组、函数等)的求值结果永远为`true`,直接返回这个对象 0 || [] // [] 1 || [] // 1 NaN || 0 // 0
&&
逻辑与操做属于短路操做,即若是第一个操做数求值结果为false
,则直接返回第一个操做数,那么就不会再对第二个操做数求值。若是第一个操做数求值为true
,则返回第二个操做数。能够用来作条件限制obj && obj.value
。只有obj
对象存在了,才会取obj.value
值。
0 && true // 0 null && [] // null NaN && null // NaN [] && {} // {}
须要注意布尔操做符存在优先级:! > && > ||
:
null || !2 || 3 && 4 // ??????你知道结果吗?实际上,代码至关于下面一行 null || (!2) || (3 && 4) // 4
相等操做符有== != === !==
四个,其中相等和不相等实行先转换类型再比较,全等和不全等实行仅比较而不转换类型。相等操做符返回布尔值true
或false
。
不一样类型操做数比较规则以下:
[] == ![] // true /* 首先,布尔操做符!优先级更高,因此被转变为:[] == false * 其次,操做数存在布尔值false,将布尔值转为数字:[] == 0 * 再次,操做数[]是对象,转为原始类型(先调用valueOf(),获得的仍是[],再调用toString(),获得空字符串''):'' == 0 * 最后,字符串和数字比较,转为数字:0 == 0 */ NaN == NaN // false NaN不等于任何值 null == undefined // true null == 0 // false undefined == 0 // false
全等和不全等在比较以前不转换类型,因此相对简单:
null === undefined // false '1' === 1 // false 0 === false // false [] === [] // false 引用类型比较相等性还要看是否指向同一个内存地址 NaN === NaN // false NaN比较特殊,不等于自身
关系操做符小于(<)、大于(>)、小于等于(<=)和大于等于(>=)比较两个值的大小,返回一个布尔值。当有一个操做数是非数值时,就会发生类型转换:
'23' <'3' // true 比较的是字符编码值 '23' < 3 // false 执行规则3 NaN > 0 // false NaN比较总会返回false null >= 0 // true 执行规则3,注意null相等性比较和关系比较不同 undefined >= 0 //false undefined执行关系比较会转化为NaN,老是返回false
三元表达式就是由条件操做符? :
组成:
a > b ? a : b; // 若是 ? 前的操做求值为 true ,则返回 a ,不然返回 b
js中等号 =
用于赋值操做,var a = 1
就是把值1
赋值给变量a
。能够和+ - * / %
构成复合赋值:
a += b // 等同于 a = a + b a -= b // 等同于 a = a - b a *= b // 等同于 a = a * b a /= b // 等同于 a = a / b a %= b // 等同于 a = a % b
逗号操做符经常使用于一条语句声明多个变量:var a = 1, b = 2, c;
js中数值是以64位格式储存的,前32位是整数,后32位是小数。位操做符会将64位的值转为32位的,因此位操做符会强制将浮点数转为整数。下面说几个经常使用的位操做符:
~
:~x
至关于-(x+1)
,能够用来代替indexOf
做为判断条件。~str.indexOf('asdf')
至关于str.indexOf('asdf')>-1
;|
:可用于将值截除为一个 32 位整数。1.11 | 0
执行结果是1
js的类型转换虽然很让人头疼,但并非无迹可寻。只要掌握了规则,就可以按规则判断出来类型到底会如何转换。而规则中很重要的一部分是,引用类型到基本类型的转换是经过ToPrimitive
抽象操做完成的。掌握了ToPrimitive
抽象操做,就掌握了类型转换的核心规则。
类型转换是很常见和很经常使用的,虽然规则多了点,但却值得去努力学习。