JavaScript運算符與類型

 

1.運算符優先級

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

2.運算符的結合性

結合性決定了擁有相同優先級的運算符的執行順序。java

3.類型

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

4.類型轉換

JavaScript的數據類型有兩類:原始類型(數字、字符串、布爾值)和對象類型,還有null、undefined(兩個特殊的原始值)。函數

類型轉換原則

  • 空數組在比較中轉換成0;一個元素的數組a轉換爲a[0],若a[0]爲數值或純數字字符串(不包含布爾值)按數值比較原則,非純數字字符串與布爾值轉化爲字符串;多個元素,數組會將方括號內的內容轉換爲字符串如[1,2] -> '1,2',不能比較,因此返回false。
  • 大小比較:不能參與比較的比較結果都是false,而不是報錯;參與比較的純數字字符串都會轉換爲數值型;布爾值與數值、布爾值與字符串(非純數字字符串不能轉換,返回false)、字符串與數值(同上)、字符串與字符串(不進行轉換,即便是純數字字符)。
  • 相等性比較:布爾值:true 等於(==)其餘形式的 1,這包含‘1’、一、[1]、['1'];false 等於其餘形式的 0 ,包含‘0’、0、['0']、[0];數值與對應的字符串是相等的。
  • 四則運算:'+'運算符:布爾+數值、布爾+布爾、數值+數值會轉換爲數值;有一個字符串,另外一個會轉換爲字符串;其餘類型,不能轉換爲數值的都會轉爲字符串。其餘運算符:參與運算的非數值類型都會試圖轉換爲爲數值類型進行運算,但只有布爾類型、純數字字符串與1個元素(該元素必須是數值型或純數字字符串,不能是布爾型)或者符合數組轉換原則的數組能夠實現轉換。不能實現轉換的參與運算後等到數值型的NaN(用函數isNaN()檢測)。
  • 條件判斷:if條件語句,while 循環語句中判斷的‘假’包含」0、''(空字符串)、null、undefined、false。
  • 邏輯運算:邏輯運算遵循了條件判斷的原則,此處應注意取值方法:"||"運算:最基本的固然是兩個操做數一真一假,必然返回真的操做數的值。若是判斷第一個操做數爲真,則不進行第二個操做數的運算直接返回第一操做數的值。若是兩個都爲假返回的是第二個操做數的值;"&&"運算:最基本的固然是兩個操做數一真一假,必然返回假的操做數的值。若是第一個操做數的值爲假,則不進行第二個操做數的運算直接返回第一個操做數的值。若是兩個都爲真則返回第二個操做數的值。"!"運算:返回值必然爲布爾類型,true 或者 false。

(1)隱形轉換

"0" == 0 //true 在比較以前字符串轉換成數字 
0 == false //true 在比較以前布爾值轉換成數字  
"0" == false //true 在比較以前字符串和布爾值都轉換成數字

(2)顯性轉換

使用Boolean()、Number()、String()或Object()函數。

(3)對象轉換爲原始值

對象到布爾值:全部的對象(包括數組和函數)都轉換爲true。對於包裝對象亦是如此:new Boolean(false)是一個對象而不是原始值,它將轉換爲true。

JavaScript中對象到字符串的轉換通過了以下這些步驟:

  • 若是對象具備toString()方法,則調用這個方法。若是它返回一個原始值,JavaScript將這個值轉換爲字符串(若是自己不是字符串的話),並返回這個字符串結果。須要注意的是,原始值到字符串的轉換在表3-2中已經有了詳盡的說明。 
  • 若是對象沒有toString()方法,或者這個方法並不返回一個原始值,那麼JavaScript會調用valueOf()方法。若是存在這個方法,則JavaScript調用它。若是返回值是原始值,JavaScript將這個值轉換爲字符串(若是自己不是字符串的話),並返回這個字符串結果。 
  • 不然,JavaScript沒法從toString()或valueOf()得到一個原始值,所以這時它將拋出一個類型錯誤異常。

在對象到數字的轉換過程當中,JavaScript作了一樣的事情,只是它會首先嚐試使用valueOf()方法:

  • 若是對象具備valueOf()方法,後者返回一個原始值,則JavaScript將這個原始值轉換爲數字(若是須要的話)並返回這個數字。 
  • 不然,若是對象具備toString()方法,後者返回一個原始值,則JavaScript將其轉換並返回。 
  • 不然,JavaScript拋出一個類型錯誤異常。

(4)加法運算符會觸發三種類型轉換:將值轉換爲原始值,轉換爲數字,轉換爲字符串,這恰好對應了JavaScript引擎內部的三種抽象操做:ToPrimitive(),ToNumber(),ToString()

http://es5.github.io/#x9.1

ToNumber()是如何將原始值轉換成數字的:

參數 結果
undefined NaN
null +0
布爾值 true被轉換爲1,false轉換爲+0
數字 無需轉換
字符串 由字符串解析爲數字.例如,"324"被轉換爲324

ToString()是如何將原始值轉換成字符串的:

參數 結果
undefined "undefined"
null "null"
布爾值 "true"  或者 "false"
數字 數字做爲字符串,好比. "1.765"
字符串 無需轉換

5.示例

運行一下代碼:

(!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]

結果爲:"sb"

這裏涉及到運算及的優先級以及類型轉換。

將上述代碼拆分,獲得:

運算符用紅色標出,其實中括號[]也是一個運算符,用來經過索引訪問數組項,另外也能夠訪問字符串的子字符,並且中括號的優先級仍是最高的。

什麼狀況下須要進行類型轉化。當操做符兩邊的操做數類型不一致或者不是基本類型(也叫原始類型)時,須要進行類型轉化:

  • 減號-,乘號*,確定是進行數學運算,因此操做數需轉化爲number類型。
  • 加號+,多是字符串拼接,也多是數學運算,因此可能會轉化爲number或string
  • 一元運算,如+[],只有一個操做數的,轉化爲number類型

執行代碼:

子表達式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了。

 

 

運算符優先級

運算符優先級 (JavaScript)

一行神奇的javascript代碼

[譯]JavaScript中,{}+{}等於多少?

JavaScript 類型轉換

javascript 類型與類型轉換

相關文章
相關標籤/搜索