首先講一下運算符的優先級,它決定了表達式中運算執行的前後順序,優先級高的運算符最早被執行。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
了。