首先讲一下运算符的优先级,它决定了表达式中运算执行的前后顺序,优先级高的运算符最早被执行。javascript
下面的表将全部运算符按照优先级的不一样从高到低排列:html
优先级 | 运算类型 | 关联性 | 运算符 |
---|---|---|---|
19 | 圆括号 |
n/a | ( … ) |
18 | 成员访问 |
从左到右 | … . … |
需计算的成员访问 |
从左到右 | … [ … ] |
|
new (带参数列表) |
n/a | new … ( … ) |
|
17 | 函数调用 | 从左到右 | … ( … ) |
new (无参数列表) | 从右到左 | new … |
|
16 | 后置递增(运算符在后) | n/a | … ++ |
后置递减(运算符在后) | n/a | … -- |
|
15 | 逻辑非 | 从右到左 | ! … |
按位非 | 从右到左 | ~ … |
|
一元加法 | 从右到左 | + … |
|
一元减法 | 从右到左 | - … |
|
前置递增 | 从右到左 | ++ … |
|
前置递减 | 从右到左 | -- … |
|
typeof | 从右到左 | typeof … |
|
void | 从右到左 | void … |
|
delete | 从右到左 | delete … |
|
14 | 乘法 | 从左到右 | … * … |
除法 | 从左到右 | … / … |
|
取模 | 从左到右 | … % … |
|
13 | 加法 | 从左到右 | … + … |
减法 | 从左到右 | … - … |
|
12 | 按位左移 | 从左到右 | … << … |
按位右移 | 从左到右 | … >> … |
|
无符号右移 | 从左到右 | … >>> … |
|
11 | 小于 | 从左到右 | … < … |
小于等于 | 从左到右 | … <= … |
|
大于 | 从左到右 | … > … |
|
大于等于 | 从左到右 | … >= … |
|
in | 从左到右 | … in … |
|
instanceof | 从左到右 | … instanceof … |
|
10 | 等号 | 从左到右 | … == … |
非等号 | 从左到右 | … != … |
|
全等号 | 从左到右 | … === … |
|
非全等号 | 从左到右 | … !== … |
|
9 | 按位与 | 从左到右 | … & … |
8 | 按位异或 | 从左到右 | … ^ … |
7 | 按位或 | 从左到右 | … | … |
6 | 逻辑与 | 从左到右 | … && … |
5 | 逻辑或 | 从左到右 | … || … |
4 | 条件运算符 | 从右到左 | … ? … : … |
3 | 赋值 | 从右到左 | … = … |
… += … |
|||
… -= … |
|||
… *= … |
|||
… /= … |
|||
… %= … |
|||
… <<= … |
|||
… >>= … |
|||
… >>>= … |
|||
… &= … |
|||
… ^= … |
|||
… |= … |
|||
2 | yield | 从右到左 | yield … |
yield* | 从右到左 | yield* … | |
1 | 展开运算符 | n/a | ... … |
0 | 逗号 | 从左到右 | … , … |
结合性决定了拥有相同优先级的运算符的执行顺序。java
JavaScript是一种无类型语言(更为精确的说,是一种松散类型,动态类型的语言)。这就是说,再声明变量时无需指定数据类型,这使JavaScript具备灵活性和简单性。在代码执行过程当中,JavaScript会根据须要自动进行类型转换。例如,若是传递给方法document.write()的是一个数字,JavaScript会自动将其转换成与之等价的字符串表示。git
javascript类型主要包括了primitive和object类型,其中primitive类型包括了:null、undefined、boolean、number、string和symbol(es6)。es6
说到类型检测主要包括了:typeof、instanceof和Object.prototype.toString.call(xxx)或{}.prototype.toString.call(xxx)。类型判断github
typeof
通常适用于判断primitive
类型的数据,在判断object
类型的数据时有时会有意想不到的结果,例如:typeof null
结果为object
。下面的表是typeof
元素符的一个结果:web
val 类型 | 结果 |
---|---|
Undefined | “undefined” |
Null | “object” |
Boolean | “boolean” |
Number | “number” |
String | “string” |
Object(原生,且没有实现 [[Call]]) | “object” |
Object(原生或者宿主且实现了 [[Call]]) | “function” |
Object(宿主且没实现 [[Call]]) | 由实现定义,但不能是 “undefined”、”boolean”、”number” 或 “string”。 |
instanceof
运算符是用于判断一个实例是否属于某一类型,例如:a instanceof Person
,其内部原理其实是判断Person.prototype
是否在a
实例的原型链中数组
Object.prototype.toString.call(xxx)或{}.prototype.toString.call(xxx),使用Object.prototype.toString会节省建立一个对象。ide
JavaScript的数据类型有两类:原始类型(数字、字符串、布尔值)和对象类型,还有null、undefined(两个特殊的原始值)。函数
"0" == 0 //true 在比较以前字符串转换成数字 0 == false //true 在比较以前布尔值转换成数字 "0" == false //true 在比较以前字符串和布尔值都转换成数字
使用Boolean()、Number()、String()或Object()函数。
对象到布尔值:全部的对象(包括数组和函数)都转换为true。对于包装对象亦是如此:new Boolean(false)是一个对象而不是原始值,它将转换为true。
JavaScript中对象到字符串的转换通过了以下这些步骤:
在对象到数字的转换过程当中,JavaScript作了一样的事情,只是它会首先尝试使用valueOf()方法:
ToNumber()是如何将原始值转换成数字的:
参数 | 结果 |
undefined | NaN |
null | +0 |
布尔值 | true被转换为1,false转换为+0 |
数字 | 无需转换 |
字符串 | 由字符串解析为数字.例如,"324"被转换为324 |
ToString()是如何将原始值转换成字符串的:
参数 | 结果 |
undefined | "undefined" |
null | "null" |
布尔值 | "true" 或者 "false" |
数字 | 数字做为字符串,好比. "1.765" |
字符串 | 无需转换 |
运行一下代码:
(!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]
结果为:"sb"
这里涉及到运算及的优先级以及类型转换。
将上述代码拆分,获得:
运算符用红色标出,其实中括号[]也是一个运算符,用来经过索引访问数组项,另外也能够访问字符串的子字符,并且中括号的优先级仍是最高的。
什么状况下须要进行类型转化。当操做符两边的操做数类型不一致或者不是基本类型(也叫原始类型)时,须要进行类型转化:
执行代码:
子表达式16:
+[] //输出0
只有一个操做数[],确定是转化为number了。[]是个数组,object类型,即对象。因此得先调用toPrimitive转化为原始类型。首先调用数组的valueOf方法,会返回自身;接下来调用数组的toString()方法,返回一个空字符串"";在调用引擎方法toNumber(),返回0。因此结果就是0。
子表达式15:
[~+""] //结果为[-1]
空串""前面有两个一元操做符,可是操做数仍是只有一个,因此,最终要转化为的类型是number。根据结合性,先计算+"",调用引擎方法toNumber(),结果为0;接下来是~,它是位运算符,做用能够记为把数字取负而后减一,因此~0就是-1 。加上中括号,因此结果为[-1]。
子表达式13:
--[~+""][+[]] //根据表达式1五、16,结果为--[-1][0]
根据优先级,--[-1][0],取数组的第0个元素,而后自减,结果为-2。
子表达式14:
[~+[]] //根据表达式1五、16,结果为[-1]
子表达式9:
此刻它已变成:-2*[-1]。
运算符是乘号*,都得转化为number。先将[-1]对象转化为原始类型"-1",再经过引擎方法toNumber()转为Number为-1。
因此结果为2.
子表达式10:
~~!+[] //从右往左计算,结果为1
+[]为0,因此!+[]就为true,一元运算符须要转化为number,因此~true等于-2,~-2等于1,。因此结果为1。
子表达式4:
结合子表达式9和10,结果为3。
表达式7:
!(~+[]) //有前面表达式得出结果为!-1
感叹号会把表达式转化为布尔类型,转化规则和js的Truthy和Falsy原则是同样的,后面跟数字的,除0之外都为true,后面跟字符串的,除空串之外都为true。这里的!-1固然就是false了。
表达式3:
false+{}。一个布尔加一个对象,那这个{}应该先转化为原始类型。调用引擎方法ToPrimitive(),结果为"[object Object]"。false与"[object Object]"相加,false先转化为字符串"false",相加得结果"false[object Object]"。
表达式1:
此时它是这样的:"false[object Object]"[3],由于这个[]能够取字符串的子字符,因此获得告终果"s"。
表达式11:
[~!+[]] //结果为[-2],见表达式10
表达式12:
~+[] //结果为-1
表达式6:表达式11和12 相乘,结果为2.
表达式5:
({}+[]) //结果为"[object Object]"
{}和[]都是对象,调用引擎方法ToPrimitive(),{}转化为原始值为"[object Object]",[]转化为原始值为""。因此结果为"[object Object]"。
子表达式2:
获得表达式"[object Object]"[2],因此结果为"b"。
问题:
为何表达式{}+[]和表达式({}+[])结果不同呢?
是由于在console中,若是你不用括号包起来的话,不认为你是在进行运算。此时{}会被解析为上一个语句的结束标记。因此{}+[]就至关因而+[]。
[]+{} //"[object Object]" {}+0 //0 ({}+0) //"[object Object]0" 0+{} //"0[object Object]"
示例:
++[[]][+[]]+[+[]] ++[0][0]+[0] 1+[0] "10"
示例:
[] + [] //"" var arr = [];arr.valueOf() === arr //true String({}) //"[object Object]" 6 + { valueOf: function () { return 2 } } //8 "abc" + { toString: function () { return "def" } } //abcdef {} + {} //NaN 一元运算符须要转为number ({} + {}) //"[object Object][object Object]"
示例:
[] == ![] //true
首先看右边的![]
,空数组转换为boolean是true
的,再进行!
,能够知道右边的![]
为false
,当一个对象和boolean进行equal
时,[]
会进行ToPrimitive
,这里就会首先调用Array.prototype.valueOf
,调用后返回的是[]
,不是原始类型,再进行Array.prototype.toString
,这里返回了""
空字符,空字符和false
进行相等比较这里就是true
了。