JavaScript中有兩個特殊的值,undefined與null。日常在寫項目時,遇到須要判斷一個值是否爲空的時候,我總會想到undefined與null。既然都是表明空值(之前我就是這麼認爲的),那麼它們有沒有區別呢?反正我是一直傻傻分不清楚,看了又忘了。最近查閱了一些資料,才發現本身之前真的是誤解它們了,知錯就改,下面總結一下它們的用法和區別。css
在介紹undefined與null以前,咱們先來了解一下ECMAScript中的數據類型。在ECMAScript中有五種簡單數據類型(也稱爲基本數據類型): Undefined、Null、Boolean、Number 和 String 。還有一種複雜數據類型——Object。express
沒錯,首字母大寫的Undefined與Null其實都屬於ECMAScript中的基本數據類型。這兩個數據類型是五種數據類型中最特殊的兩個類型,由於它們都只有惟一的一個值,分別是undefined與null,就是咱們今天要介紹的兩個主角。函數
上面咱們說過了,Undefined類型只有一個值,就是特殊的undefined,在兩種狀況下咱們會獲得undefined:post
聲明瞭一個變量,但未對其初始化時,這個變量的值就是undefined。學習
var data;
console.log(data === undefined); //true
複製代碼
那麼我麼是否能夠顯式地把一個變量初始化爲undefined呢,答案是能夠的。測試
var data = undefined;
console.log(data === undefined); //true
var value = 1;
console.log(data); //1
value = undefined;
console.log(data === undefined); // true
複製代碼
通常而言,咱們不存在須要顯式地把一個變量設置爲undefined值的狀況,由於對於未經初始化的值默認就會取得undefined值,而已經初始化的值再將其賦值爲undefined來表示空值是沒有意義且不可取的。何況字面值undefined的主要目的以用於比較,來區分空對象指針(後面咱們會介紹到這指的就是null)與未經初始化的變量的狀況。ui
對未定義的變量執行typeof操做符也會返回undefinedlua
//data變量未定義
var value;
console.log(typeof data); // "undefined"
console.log(typeof value); // "undefined"
複製代碼
這裏咱們沒有使用===
來判斷,由於對於還沒有聲明過的變量,咱們只能執行一項操做,即便用typeof操做符檢測其數據類型,使用其餘的操做都會報錯。spa
//data變量未定義
console.log(data === undefined); //報錯
複製代碼
結果代表對未初始化和未聲明的變量執行typeof操做符都返回了undefined值,這個結果有其邏輯上的合理性。由於雖然這兩種變量從技術角度看有本質區別,但實際上不管對哪一種變量也不可能執行真正的操做。設計
還有其餘幾種狀況也會返回undefined,好比一個函數若是沒有使用return語句指定返回值,就會返回一個undefined值,或者調用函數時沒有傳參數值,參數一樣也會被初始化爲undefined值。這些都是屬於上面兩種狀況在代碼中的體現,這裏就不單獨解釋了。
從上面的例子咱們能夠看出,不管咱們是否初始化過變量,均可以給變量賦值爲undefined。其實這裏用於賦值的undefined不是一個值,它是一個屬性名,undefined是全局對象的一個屬性,也就是說,它是全局做用域的一個變量,即window.undefined
,而window.undefined
這個屬性的值纔是前面所說的原始值undefined。data = undefined;
這就至關於把一個變量window.undefined
的值賦值給另外一個變量data
,這個值就是原始值undefined。其實在JavaScript代碼中,咱們看到的undefined大多數狀況指的都是window.undefined
(本篇文章中多數狀況下也是,原始值undefined除外),原始值undefined多數狀況下只存在於文檔或規範中,不存在於JavaScript代碼中(具體能夠理解爲代碼中參與判斷、比較或賦值的都是window.undefined
,而在控制檯中輸出,或函數中返回的則是原始值undefined)。
console.log(window.undefined); //原始值undefined
複製代碼
注意,在ES3以前實際上是沒有原始值undefined這個值的,第三版引入這個值,實際上是爲了正式區分空對象指針(後面咱們會介紹到這指的就是null)與未經初始化的變量。在ES3中,window.undefined
就是一個普通的屬性,咱們徹底能夠把它的值改成任何真值。但從ES5以後,window.undefined
成了一個不可寫,不可配置的數據屬性,它的值永遠是undefined。
你們可能注意到了,上面我提到的是在大多數狀況下undefined指的都是window.undefined
,那還有什麼其餘狀況嗎?其實在ECMAScript中,undefined不是一個保留字,這意味着什麼呢?也就是說咱們能夠將undefined做爲一個局部變量來使用,就像局部做用域中任何其餘普通變量同樣,沒有任何特殊性,咱們能夠對其賦予任何類型的值。
(function() {
var undefined = 'not is undefined';
console.log(undefined); //"not is undefined"
console.log(typeof undefined) // "string"
})()
複製代碼
咱們能夠看到undefined的值和類型都已經改變,這樣的作法是很是不友好的,這樣會使咱們的代碼難以維護和排錯。
如何判斷一個變量是否爲undefined,這裏有兩種方法。
使用嚴格相等符===
或不相等操做符!==
來決定一個變量是否擁有值,這裏不使用標準相等操做符==
,是由於標準相等符還會會檢查變量是否是爲null,可是嚴格相等操做符不會檢查。null不等同於undefined,這點咱們會在後面講到。
使用typeof操做符,這種方式咱們在上面已經使用過了,對未定義的變量檢測時只能使用這種方式,要否則會出現報錯。
上面咱們提到過了,undefined做爲局部變量使用是能夠被重寫的,那麼若是咱們使用下面這種判斷方式,是有風險的。
if(data === undefined){
//do something
}
複製代碼
那麼咱們怎樣作才能確保萬無一失呢?讓咱們先來了解一下void運算符,官方文檔是這樣解釋的:
The void operator evaluates the given expression and then returns undefined.
void 運算符 對給定的表達式進行求值,而後返回 undefined
什麼意思呢?就是使用void對後面的表達式求值,不管結果是多少,都會返回原始值undefined。所以咱們能夠用void 0
來代替undefined進行判斷,由於void 0
始終返回的都是原始值undefined。
var data;
console.log(data === void 0); //true
複製代碼
Null類型是第二個只有一個值的數據類型,這個特殊的值就是null。值 null 是一個字面量,它不像undefined 是全局對象的一個屬性。從邏輯角度來看,null值表示一個空對象指針,指示變量未指向任何對象。把 null 做爲還沒有建立的對象,也許更好理解。在 APIs 中,null 常在返回類型是對象,但沒關聯值的地方使用,就像下面一個例子。
//document.getElementById() 能夠返回對擁有指定 ID 的第一個對象的引用
var $container = document.getElementById("container"); // 注意:container是不存在的
console.log($container); // null
複製代碼
當咱們使用typeof操做符檢測null值,咱們理所應當地認爲應該返"Null"類型呀,可是事實返回的類型倒是"object"。
var data = null;
console.log(typeof data); // "object"
複製代碼
是否是很奇怪?其實咱們能夠從兩方面來理解這個結果
一方面從邏輯角度來看,null值表示一個空對象指針,它表明的其實就是一個空對象,因此使用typeof操做符檢測時返回"object"也是能夠理解的。
另外一方面,其實在JavaScript 最初的實現中,JavaScript 中的值是由一個表示類型的標籤和實際數據值表示的。對象的類型標籤是 0。因爲 null 表明的是空指針(大多數平臺下值爲 0x00),所以,null的類型標籤也成爲了 0,typeof null就錯誤的返回了"object"。在ES6中,當時曾經有提案爲歷史平凡, 將type null的值糾正爲null, 但最後提案被拒了,因此仍是保持"object"類型。
null的判斷可使用嚴格相等符===
或不相等操做符!==
判斷,不使用標準相等符的緣由是由於undefined會影響判斷結果。和undefined不同,不能使用typeof來判斷一個值是否爲null,緣由上邊已經講了,使用typeof來檢測null會返回"object",這樣的話咱們是沒辦法判斷的。
if(data === null){
console.log("data中沒有保存對象引用!");
}
複製代碼
那麼咱們在什麼狀況下須要將變量賦值爲null呢?這裏我想到的有兩種狀況。
若是定義的變量在未來用於保存對象,那麼最好將該變量初始化爲null,而不是其餘值。換句話說,只要意在保存對象的變量尚未真正保存對象,就應該明確地讓該變量保存null值,這樣有助於進一步區分null和undefined。
當一個數據再也不須要使用時,咱們最好經過將其值設置爲null來釋放其引用,這個作法叫作解除引用。不過解除一個值的引用並不意味着自動回收改值所佔用的內存。解除引用的真正做用是讓值脫離執行環境,以便垃圾收集器在下次運行時將其回收。解除引用還有助於消除有可能出現的循環引用的狀況。這一作法適用於大多數全局變量和全局對象的屬性,局部變量會在它們離開執行環境時(函數執行完時)自動被解除引用。
實際上undefined值是派生自null值的,所以ECMA-262規定對它們的相等性測試要返回true:
console.log(null == undefined); //true
複製代碼
由於使用的是標準相等符==
,這個操做符出於目的會轉換其操做數爲相同類型後再作比較,若是咱們使用嚴格相等符比較,咱們會發現它們是不相等的,由於嚴格相等符不會進行類型轉換,然而undefined與null屬於不一樣的類型,因此不相等。
console.log(null === undefined); //false
複製代碼
儘管null和undefined有這樣的關係,但上面咱們已經提到過了,它們的用途徹底不一樣,咱們在日常使用時必定要學會區分。
《JavaScript高級程序設計(第三版)》
JavaScript中undefined和null的區別
JavaScript 參考文檔 null
JavaScript 參考文檔 undefined
JavaScript 參考文檔 typeof
(void 0) 與 undefined 之間的小九九
花了一些時間來總結undefined與null的用法和區別,之前一直沒有注意,通過此次總結才發現它們所表明的的意義和用法徹底不一樣。可能在日常使用的時候咱們不須要考慮這麼多問題,但經過從新看一些東西,總結一些東西,經過這樣的方式對我來講收穫挺大的。本篇文章純屬我的的學習總結,若是文章中存在錯誤的地方,但願你們能夠向我指出。
本篇文章發表在我的博客CavsZhouyou's Blog上,歡迎你們參觀!