JavaScript深刻理解之undefined與null

配圖7-1

寫在前面

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,在兩種狀況下咱們會獲得undefined:post

  1. 聲明瞭一個變量,但未對其初始化時,這個變量的值就是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

  2. 對未定義的變量執行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值。這些都是屬於上面兩種狀況在代碼中的體現,這裏就不單獨解釋了。

全局屬性 window.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

你們可能注意到了,上面我提到的是在大多數狀況下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 判斷

如何判斷一個變量是否爲undefined,這裏有兩種方法。

  1. 使用嚴格相等符===或不相等操做符!==來決定一個變量是否擁有值,這裏不使用標準相等操做符==,是由於標準相等符還會會檢查變量是否是爲null,可是嚴格相等操做符不會檢查。null不等同於undefined,這點咱們會在後面講到。

  2. 使用typeof操做符,這種方式咱們在上面已經使用過了,對未定義的變量檢測時只能使用這種方式,要否則會出現報錯。

void 0

上面咱們提到過了,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。值 null 是一個字面量,它不像undefined 是全局對象的一個屬性。從邏輯角度來看,null值表示一個空對象指針,指示變量未指向任何對象。把 null 做爲還沒有建立的對象,也許更好理解。在 APIs 中,null 常在返回類型是對象,但沒關聯值的地方使用,就像下面一個例子。

//document.getElementById() 能夠返回對擁有指定 ID 的第一個對象的引用

   var $container = document.getElementById("container"); // 注意:container是不存在的

   console.log($container); // null
複製代碼

typeof 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 判斷

null的判斷可使用嚴格相等符===或不相等操做符!==判斷,不使用標準相等符的緣由是由於undefined會影響判斷結果。和undefined不同,不能使用typeof來判斷一個值是否爲null,緣由上邊已經講了,使用typeof來檢測null會返回"object",這樣的話咱們是沒辦法判斷的。

if(data === null){
       console.log("data中沒有保存對象引用!");
   }
複製代碼

null 使用

那麼咱們在什麼狀況下須要將變量賦值爲null呢?這裏我想到的有兩種狀況。

  • 若是定義的變量在未來用於保存對象,那麼最好將該變量初始化爲null,而不是其餘值。換句話說,只要意在保存對象的變量尚未真正保存對象,就應該明確地讓該變量保存null值,這樣有助於進一步區分null和undefined。

  • 當一個數據再也不須要使用時,咱們最好經過將其值設置爲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上,歡迎你們參觀!

相關文章
相關標籤/搜索