天天看點

JavaScript深入了解之undefined與null

JavaScript深入了解之undefined與null

寫在前面

JavaScript中有兩個特殊的值,undefined與null。平常在寫項目時,遇到需要判斷一個值是否為空的時候,我總會想到undefined與null。

既然都是代表空值(以前我就是這麼認為的),那麼它們有沒有差別呢?反正我是一直傻傻分不清楚,看了又忘了。最近查閱了一些資料,才發現自己以前真的是誤解它們了,知錯就改,下面總結一下它們的用法和差別。

基本資料類型

在介紹undefined與null之前,我們先來了解一下ECMAScript中的資料類型。在ECMAScript中有五種簡單資料類型(也稱為基本資料類型): Undefined、Null、Boolean、Number 和 String 。還有一種複雜資料類型——Object。

沒錯,首字母大寫的Undefined與Null其實都屬于ECMAScript中的基本資料類型。這兩個資料類型是五種資料類型中最特殊的兩個類型,因為它們都隻有唯一的一個值,分别是undefined與null,就是我們今天要介紹的兩個主角。

Undefined 類型

定義

上面我們說過了,Undefined類型隻有一個值,就是特殊的undefined,在兩種情況下我們會得到undefined:

1、聲明了一個變量,但未對其初始化時,這個變量的值就是undefined。

​var​

​​

​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)與未經初始化的變量的情況。

2、對未定義的變量執行typeof操作符也會傳回undefined

​//data變量未定義​

​​

​var​

​​

​console.log(typeof data); // "undefined"​

​​

​console.log(typeof value); // "undefined"​

這裡我們沒有使用​

​===​

​來判斷,因為對于尚未聲明過的變量,我們隻能執行一項操作,即使用typeof操作符檢測其資料類型,使用其他的操作都會報錯。

​//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​

​​

​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有這樣的關系,但上面我們已經提到過了,它們的用途完全不同,我們在平常使用時一定要學會區分。

參考文獻

寫在最後

繼續閱讀