1.先了解一下基本類型和複雜類型劃分的依據
JS中的值有兩種類型:原始類型(Primitive)、對象類型(Object)。原始類型包括:Undefined、Null、Boolean、Number和String等五種。這兩大類别的資料存儲方式是不一樣的。
存儲空間的時空關系可以概括為空間大,通路起來就慢,反之亦然。堆比棧大,棧比堆的運算速度快,
對象是一個複雜的結構,并且可以自由擴充,如:數組可以無限擴充,對象可以自由添加屬性。将它們放在堆中是為了不影響棧的效率。而是通過引用的方式查找到堆中的實際對象再進行操作。
相對于對象類型而言,原始類型就比較穩定,并且它隻占據很小的記憶體。不将原始類型放在堆是因為通過引用到堆中查找實際對象是要花費時間的,而這個綜合成本遠大于直接從棧中取得實際值的成本。是以簡單資料類型的值直接存放在棧中。`
順帶說一下undefined和null的使用差別:
假如你打算把一個變量賦予對象類型的值,但是現在還沒有指派,那麼你可以用null表示此時的狀态(證據之一就是typeof null 的結果是'object');相反,假如你打算把一個變量賦予原始類型的值,但是現在還沒有指派,那麼你可以用undefined表示此時的狀态。
2.進行==比較時資料的轉換規則
==運算規則的準備描述如下:
看完之後有沒有感覺有點腦殼疼,這樣的描述很難讓人在實踐中使用,是以很有必要對上述規則進行簡化概括:
- undefined == null,結果是true。且它倆與所有其他值比較的結果都是false。
- String == Boolean,需要兩個操作數同時轉為Number。
- String/Boolean == Number,需要String/Boolean轉為Number。
- Object == Primitive,需要Object轉為Primitive(具體通過valueOf和toString方法)。
那麼Boolean,Number,Primitive的轉換規則是:
(1)其它類型轉換成Boolean的規則是:
(2)其它類型轉換成Number類型的規則
object類型一般要先轉換成string類型,接着才進行Number類型的轉換。字元串轉化為數字的規則規範中描述得很複雜,但是大緻說來,就是把字元串兩邊的空白字元去掉,然後把兩邊的引号去掉,看它能否組成一個合法的數字。如果是,轉化結果就是這個數字;否則,結果是NaN。
Number('123') // 結果123
Number('1.2e3') // 結果1200
Number('123abc') // 結果NaN
Number('\r\n\t123\v\f') // 結果123
當然也有例外,比如空白字元串轉化為數字的結果是0。即:
Number('') // 結果0
Number('\r\n\t \v\f') // 結果0
需要補充說明的是:
(3)對象類型向primitive類型轉換的規則是:
為什麼要區分原始類型和複雜類型?
原始類型是一種單純的類型,它們直接了當、容易了解。然而缺點是表達能力有限,難以擴充,是以就有了對象。對象是屬性的集合,而屬性本身又可以是對象。是以對象可以被構造得任意複雜,足以表示各種各樣的事物。
但是,有時候事情複雜了也不是好事。比如一篇冗長的論文,并不是每個人都有時間、有耐心或有必要從頭到尾讀一遍,通常隻了解其中心思想就夠了。于是論文就有了關鍵字、概述。JavaScript中的對象也一樣,我們需要有一種手段了解它的主要特征,于是對象就有了toString()和valueOf()方法。
這兩種方法的差別是:
toString()方法用來得到對象的一段文字描述;而valueOf()方法用來得到對象的特征值。
toString()方法傾向于傳回一個字元串。valueOf()方法傾向于傳回一個數字——盡管内置類型中,valueOf()方法傳回數字的隻有Number和Date。
對于所有非日期對象來說,對象到原始值的轉換基本上是對象到數字的轉換
一般來說,對象到數字的轉換經過了如下過程:
1.如果對象具有valueOf()方法,後者傳回一個原始值,則js将這個原始值轉換成數字,并傳回這個數字。
2.否則,如果對象具有toString()方法,後者傳回一個原始值,則js将轉換并傳回。(首先js轉換成相應的字元串原始值,再繼續将這個原始值轉換成相應的數字類型,再傳回數字)
3.否則,js抛出一個類型錯誤異常。
一般來說,對象到字元串的轉換經過了如下步驟:
1.如果對象具有toString()方法,則調用這個方法。如果它傳回一個原始值,js将這個值轉換成字元串,并返還這個字元串結果。
2.如果對象沒有toString()方法,或者toString并不傳回一個原始值,那麼js将調用valueOf()方法。
3.否則,js無法從toString()或者valueOf()獲得一個原始值,是以這時它将抛出一個類型錯誤異常。
下圖第一列和第二列是其它類型調用toString方法時的轉換規則
需要說明的是: toString方法會将{}轉換成"[object Object]",将function(){}轉換成"function(){}"
其它類型調用valueOf方法的轉換規則是:
經過層層深入剖析,現在你應該了解前面各種資料類型進行 == 運算時的隐式運作規則了吧。現在再碰到如下的問題,是不是感覺思路很清晰
[]==[] //false
[]==![] //true
{}==!{} //false 實際上被解析成 { ' } == !{ ' }
{}==![] //Uncaught SyntaxError: Unexpected token == 表達式不能以{開頭 {}是個語句塊,後面跟== ![]就變成了一種文法錯誤的語句塊
// 在文法解析的時候,如果一個語句以「{」開頭,就隻把它解釋成語句塊。換用形式文法的說法,就是「表達式語句不能以『{』開頭」。對表達式語句開頭的另一個限制——限制「function」出現在開頭——同理。
![]=={} //false
[]==!{} //true
undefined==null //true
[] == [] // false 因為它們的引用位址不一樣
由上面的比較可以得出:
對任何一種類型進行取反 會得到一個boolean類型的值
[]在與不同的類型對比的時候,會轉換成0
{}在與不同的類型對比的時候,會轉換成NaN,
不同類型的比較最終都被轉換成number類型的比較
參考文章
[1] 一張圖徹底搞懂JavaScript的==運算
[2] 為什麼控制台列印{}+[]===[]+{}為false?