[譯]Understanding javascript's 'undefined'

和其餘語言相比,javascript中的對於undefined的理解仍是有點讓人困惑的。特別是試着理解ReferenceErrors錯誤("x is not defined")以及在編碼過程當中如何去避免這些錯誤總讓人感到比較困惑。javascript

這篇文章是我整理的關於這個知識點的內容。若是你對於javascript中的變量以及屬性還不是很熟悉的話,你能夠看看我以前寫的[文章]()java

undefined是什麼?

Javascript中,存在着Undefined(基本類型),undefined(值),以及defedined(變量)web

Undefined(基本類型)是js內置的基本類型之一(String, Number, Boolean, Null, Object)瀏覽器

undefined(值)是一個原始值,是未定義類型的基礎值。任何未被賦值的屬性值均可以假設爲undefined。無返回值或者返回值爲空的函數最後執行獲得的值都未undefined。未設定的函數參數值也爲undefined.wordpress

var a;
    typeof a; // undefined
    
    window.b;
    typeof window.b; // undefined
    
    var c = (function() {})();
    typeof c; // undefined
    
    var d = (function(e) {return e})();
    typeof d; // undefined

undefined(變量)是初始值爲undefined的全局屬性,既然它是個全局屬性,那麼咱們也能夠經過變量來獲取它。例如我在這篇文章裏面像這樣將它做爲一個變量來獲取它。函數

typeof undefined; // undefined
    
    var f = 2;
    f = undefined;  //從新將變量f賦值爲變量undefined
    typeof f; // undefined

在ECMA3當中,它的值能夠從新被賦值:編碼

undefined = 'washing machine';
    typeof undefined;  // string
    
    f = undefined;
    typeof f; // string
    f; // 'washing machine'

不用說,將undefined從新賦值是一個比較糟糕的用法。事實上再ECMA5當中這種作法也是不被容許的。code

And then there's null?

大體上咱們瞭解這二者之間的區別,可是仍是須要從新複述一遍:undefinednull相比,undefined是一個原始值,它表示一個缺省值。undefinednull之間惟一類似的地方就是它們都能被經過類型轉換成false對象

So what's a RefernceError?

一個ReferenceError表示編譯器檢測到一個無效的引用值。blog

在實際狀況中,ReferenceError每每是在Javascript獲取一個未被賦值的引用時被拋出。
注意下在不一樣瀏覽器中,ReferenceError如今的不一樣的語法錯誤提示信息。正如咱們看到的,在不一樣瀏覽器中,錯誤信息並不是特別的清楚明瞭。

alert(foo);
    
    //FF/Chrome: foo is not defined
    //IE: foo is undefined
    //Safari: can't find variable foo

Still not clear ..."unresolvable reference"?

在ECMA標準中,一個引用值包含一個引用名稱及一個基本值。

若是這個引用是一個屬性,那麼基礎值及這個引用分別位於點號的兩側:

window.foo; // base value = window, reference name = foo;
    a.b; //base value = a, reference name = b;
    myObj['create']; //base value = myObj, reference name = create;
    //Safari, Chrome, IE8+ only
    Object.defineProperty(window, 'foo', {value: 'hello'});
    //base value = window. reference name = foo;

對於引用變量來講,基礎值是當前執行上下文的變量對象。全局上線文的變量對象就是全局對象本身(瀏覽器當中是window)。任何一個函數上下文都有一個被稱爲活動對象的變量對象。(這個活動對象取決於調用這個函數的執行context)

var foo;    //base value = window, reference name = foo;
    function a() {
        var b;  // base value = <code>ActivationObject</code>, reference name = b;
    }

若是一個引用的基礎值是undefined的話,那麼這個引用就被認爲unresolvable

所以,若是點號前面的值爲undefined,那麼這個屬性引用就是unresolved。下面的這個例子就會拋出一個ReferenceError的錯誤,可是這並不是是由於TypeError在此以前就被拋出。這是由於一個屬性的基礎值是屬於CheckObjectCoercible,當試着將一個Undefined類型的轉化爲一個Object,那麼這種狀況下會拋出TypeError;

var foo;    
    foo.bar;    //TypeError (base value, foo, is undefined)
    bar.baz;    //ReferenceError (bar is unresolvable)
    undefined.foo   //TypeError (base value is undefined)

若是使用var關鍵字,那麼將會確保變量對象始終有基礎值,那麼就不會出現引用變量unresolvable的狀況。

當引用被定義的時候既不是屬性值也不是變量的時候將會拋出一個ReferenceError:

foo;   //ReferenceError

Javascript檢測到引用名foo沒有明確的基礎值,所以就會去尋找屬性名爲foo的變量對象。沒有找到的話,就會認爲引用名foo沒有基礎值並拋出ReferenceError的錯誤。

But isn't foo just a undeclared variable?

技術上來講不是。儘管有時候咱們以爲undeclared variable是有利用咱們去排查bug。可是事實上若是一個變量未被聲明的話也就不是一個變量。

What about implicit globals?

未經過var關鍵字聲明的標識符將會默認當作全局變量而被建立,但這隻會在這些標識符被賦值的狀況下才會生效。

function a() {
        alert(foo);     // ReferenceError
        bar = [1, 2, 3]; // no error, bar is global
    }
    a();
    bar;    // [1, 2, 3]

這確實讓人有點困惑。若是Javascript檢測到unresolvable的引用就直接拋出ReferenceErrors就更好了。(事實上在嚴格模式下javascript確實是這樣作的)

When do I need to code against ReferenceErrors?

若是你的代碼寫的很合理。咱們發如今典型的用法中只有一種方式會遇到unresolvable reference: 當使用屬性或者變量的句法不正確的時候。在大多數狀況下,若是你能確保聲明變量的時候使用var關鍵字時便可避免這種狀況。在代碼運行過程當中惟一可能會遇到的狀況就是引用的變量僅僅存在於部分瀏覽器或者第三方的代碼當中。

一個比較好的例子就是console.在webkit瀏覽器中,console是內置的,console這個屬性能夠在任何地方獲取到。Firefoxconsole屬性取決於Firebug是否被安裝以及被打開使用。IE7下沒有console,IE8下的console屬性僅存在於IE Developer Tools被啓動的狀況下。Opera明顯是有console屬性的,可是我歷來沒用使用過。

最後的結果就是,下面的代碼在瀏覽器中運行時仍是有可能會拋出ReferenceError的錯誤。

console.log(new Date());

How do I code against variables that may not exist?

一種方式就是去經過使用typeof關鍵字去嗅探一個unresolvable reference,避免拋出ReferenceError錯誤:

if (typeof console != 'undefined') {
        console.log(new Date());
    }

然而這種方法對我來講太囉嗦了,更不用說合理了。我是比較反對使用typeof去進行類型檢測的。

幸運的是還有另一種方式:咱們已經知道基礎值被定義了,可是屬性未被定義是不會拋出ReferenceErrorconsole是全局對象的屬性值,所以咱們能夠這樣作:

window.console && console.log(new Date());

事實上在全局環境下僅僅只須要檢測變量是否存在(函數中也存在着執行上下文,你能夠決定哪些變量能夠存在於你的函數當中)。所以理論上至少你能夠避免使用typeof來消除ReferenceError的狀況。

原文連接

相關文章
相關標籤/搜索