首先在這之前,我并不知道undefined == null
且 undefined !== null
,如果你也不知道,那么可以繼續閱讀試著了解,如果已經知道就忽略此文吧
本文所述內容:
-
undefined
與null
的區別 -
javascript
數據類型轉換 -
==
與===
的區別(使用==
時發生了什么)
1. undefined
與null
的區別
我首先看了這篇文章:理解Javascript_02_理解undefined和null
個人總結:雖然覺得這篇文章的有些理解是錯的,但是給我很好的開了個頭。他的表述是:
- Undefined代表沒有賦值的基本數據類型
- Null代表沒有賦值的引用數據類型
我們都知道,基本數據類型數據存在棧里,引用數據類型數據存在堆里,棧比堆運算速度快,堆比棧存的多。這篇文章認為,為了的這個計算效率原因,再加上“undefined 實際上是從值 null 派生來的”,基本數據類型是對應引用數據類型的子類,只不過是為了提高效率,將其放在棧內存中而已。這是錯誤的理解。
基本數據類型也就是原始數據類型(primitive),引用數據類型就是復合數據類型(complex),“undefined 實際上是從值 null 派生來的”個人覺得這句話只是說的字面上的派生,之所以這么理解undefined和null
,是因為“歷史原因”,看了下百科關于JavaScript的發明人——Brendan Eich,里面有說他的設計思路:
javascript為什么要用2個表達方式來表述空這個概念?摘一段引用:
1.null 和 undefined在現代JS語義里面是有明確區別的:
- null 表示一個值被定義了,定義為“空值”;
- undefined 表示根本不存在定義。
所以設置一個值為 null 是合理的
2.JS 中同時存在 undefined 和 null 是合理的
首先在 Java 中不存在 undefined 是很合理的:Java 是一個靜態類型語言,對于 Java 來說不可能存在一個“不存在”的成員(不存在的話直接就編譯失敗了),所以只用 null 來表示語義上的空值。而 JavaScript 是一門動態類型語言,成員除了表示存在的空值外,還有可能根本就不存在(因為存不存在只在運行期才知道),所以這就要一個值來表示對某成員的 getter 是取不到值的。
java中null只是存粹的表示空,且不是一種任何數據類型,作為動態類型的js來說,有些值只有在運算時才能知道存不存在,必須要有一個類型來表示,所以就出現了undefined。另外js里的:typeof null === "object"
應該是一個設計失誤。
此處參考undefined與null的區別@ RedNax評論
最后undefined == null
,就是規范定義的結果,規范第2條中說:If x is null and y is undefined, return true.
此處規范中文版,所以說undefined == null
并沒有什么可討論的,規范就是這么定義的,至于undefined !== null
因為這就是2個不同類型的數據,肯定不相等。
2.javascript數據類型轉換
討論類似undefined == null
,[] !== []
有何實際意義?其實,我們討論這些問題一定要去了解其中涉及的原理,討論單個點確實無意義。就像第一個問題:涉及的點就有類型轉換和數據類型和內存管理。
我們這里討論數據類型轉換內容:
- parseInt()和parseFloat()
- 強制轉換
- 隱式轉換
parseInt()和parseFloat()
內容 | parseInt | parseFloat |
---|---|---|
基數 | 默認十進制,八進制,十六進制可選 | 只能是十進制 |
共同點 | 都是將有效字符前的字符轉化為數字 | 都只有對String類型調用這些方法,其他類型或者該字符串不是一個有效的字符串都是返回NaN
|
強制轉換
強制轉換主要指使用Number、String和Boolean三個構造函數,手動將各種類型的值,轉換成數字、字符串或者布爾值。
此處查看數據類型轉換即可
里面未涉及的點:valueOf()
和 toString()
,每個對象都有自己的valueOf()
和 toString()
,并且做類型轉換時自動調用。
1.Object.prototype.valueOf() Object.prototype.toString()
默認情況下,對象的valueOf方法返回對象本身
toString方法返回returns "[object type]"
,這里的type為object
Every object has a toString() method that is automatically called when the object is to be represented as a text value or when an object is referred to in a manner in which a string is expected.
2.Array.prototype.valueOf() Array.prototype.toString()
同樣,數組的valueOf方法返回的也是它本身
let arr = [1, 3, 4]
arr.valueOf() // [1, 3, 4]
toString返回的是A string representing the elements of the array.
let arr = [1, 2]
arr.toString() // 1,2
3.Function.prototype.valueOf() Function.prototype.toString()
函數的valueOf方法返回的也是它本身
這里的toString返回A string representing the source code of the function.
由數據類型轉換一文可知:
做Number強制轉換并且參數是對象時,轉換經歷了3個步驟:
如果valueOf返回的是基本數據類型,則直接用基本數據類型的轉換規則;如果返回的是對象,則再調用toString,得到string基本數據類型后,再轉換。
對于一般的3中引用類型的數據,valueOf都會返回其本身,除非重寫valueOf方法。簡單來說,Number做強制轉換時,直接用toString方法后再做轉換就好了。
同理,String做強制轉換時,是先調用的toString,如果返回的不是基本數據類型,則再調用valueOf。所以說,一般情況下valueOf都是返回的本身,沒啥用,除非重寫valueOf,做實事的都是toString
對于Boolean,所有對象都返回true。
總結:我們了解Boolean([])
,String([])
有何實際意義?真正用到的場景我想就是隱式轉換了吧
3.== 與 ===的區別(隱式轉換)
我們都知道,在做==比較時,不同類型的數據會先轉換成一致后再做比較:
'3' == 3 // true
0 == false // true
在做===時,如果類型不一致就直接返回false了,一致的才會繼續比較
其實,隱式轉換包含很多場景,除了上面的==外,還有
1 + '3' // '13'
let a = 2 > '1' ? 'string' : false
'4' - '2'
+new Date
+ {a: 1} // NaN
隱式轉換,基本類型之間到底是誰轉誰呢?
就拿==來說,有以下幾種情況n1 == n2
:
- 如果有一個操作數為boolean值,則先轉為數值后再做比較;
- 如果n1是字符串,n2是數值,也是轉換為數值再比較;
- 如果有一個是對象,一個不是,則先調用Number做強制轉換,得到基本數據類型后再比較;
也就是說,如果n1,n2類型不一致(出現boolean或數值),會優先轉為數值后再進行比較。
false == 1 // false 轉為0再比較
false == '2' // false轉為0,'2'轉為1
'3' == 2 // '3'轉為3再比較
其他除了==情況下,都是根據實際情況來定,比如:
- 在if或者三目運算符中,那肯定是轉為boolean了,除了以下六個值為false,其他都是自動轉為true
undefined
null
-0
0或+0
NaN
''(空字符串)
詳細參考數據類型轉換.
關于==時的隱式類型轉換規則時,聊一聊 JS 中的『隱式類型轉換』跟我的想法一致:
總結
了解了這么多,無非就是為了知道語言自身的規則,好讓我們能更好的規避錯誤。
當然里面涉及的點,還是很有必要了解與熟知的:js數據類型
,強制轉換
,js歷史
。
說到隱式轉換,建議在所有使用條件判斷的時候都使用全等運算符 === 來進行條件判斷,全等運算符會先進行數據類型判斷,并且不會發生隱式類型轉換。