和其餘語言相比,javascript
中的對於undefined
的理解仍是有點讓人困惑的。特別是試着理解ReferenceErrors
錯誤("x is not defined")以及在編碼過程當中如何去避免這些錯誤總讓人感到比較困惑。javascript
這篇文章是我整理的關於這個知識點的內容。若是你對於javascript
中的變量以及屬性還不是很熟悉的話,你能夠看看我以前寫的[文章]()java
在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
大體上咱們瞭解這二者之間的區別,可是仍是須要從新複述一遍:undefined
和null
相比,undefined
是一個原始值,它表示一個缺省值。undefined
和null
之間惟一類似的地方就是它們都能被經過類型轉換成false
。對象
一個ReferenceError
表示編譯器檢測到一個無效的引用值。blog
在實際狀況中,ReferenceError
每每是在Javascript
獲取一個未被賦值的引用時被拋出。
注意下在不一樣瀏覽器中,ReferenceError
如今的不一樣的語法錯誤提示信息。正如咱們看到的,在不一樣瀏覽器中,錯誤信息並不是特別的清楚明瞭。
alert(foo); //FF/Chrome: foo is not defined //IE: foo is undefined //Safari: can't find variable foo
在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
的錯誤。
foo
just a undeclared variable?技術上來講不是。儘管有時候咱們以爲undeclared variable
是有利用咱們去排查bug
。可是事實上若是一個變量未被聲明的話也就不是一個變量。
未經過var
關鍵字聲明的標識符將會默認當作全局變量而被建立,但這隻會在這些標識符被賦值的狀況下才會生效。
function a() { alert(foo); // ReferenceError bar = [1, 2, 3]; // no error, bar is global } a(); bar; // [1, 2, 3]
這確實讓人有點困惑。若是Javascript
檢測到unresolvable
的引用就直接拋出ReferenceErrors
就更好了。(事實上在嚴格模式下javascript
確實是這樣作的)
若是你的代碼寫的很合理。咱們發如今典型的用法中只有一種方式會遇到unresolvable reference
: 當使用屬性或者變量的句法不正確的時候。在大多數狀況下,若是你能確保聲明變量的時候使用var
關鍵字時便可避免這種狀況。在代碼運行過程當中惟一可能會遇到的狀況就是引用的變量僅僅存在於部分瀏覽器或者第三方的代碼當中。
一個比較好的例子就是console
.在webkit
瀏覽器中,console
是內置的,console
這個屬性能夠在任何地方獲取到。Firefox
中console
屬性取決於Firebug
是否被安裝以及被打開使用。IE7
下沒有console
,IE8
下的console
屬性僅存在於IE Developer Tools
被啓動的狀況下。Opera
明顯是有console
屬性的,可是我歷來沒用使用過。
最後的結果就是,下面的代碼在瀏覽器中運行時仍是有可能會拋出ReferenceError
的錯誤。
console.log(new Date());
一種方式就是去經過使用typeof
關鍵字去嗅探一個unresolvable reference
,避免拋出ReferenceError
錯誤:
if (typeof console != 'undefined') { console.log(new Date()); }
然而這種方法對我來講太囉嗦了,更不用說合理了。我是比較反對使用typeof
去進行類型檢測的。
幸運的是還有另一種方式:咱們已經知道基礎值被定義了,可是屬性未被定義是不會拋出ReferenceError
。console
是全局對象的屬性值,所以咱們能夠這樣作:
window.console && console.log(new Date());
事實上在全局環境下僅僅只須要檢測變量是否存在(函數中也存在着執行上下文,你能夠決定哪些變量能夠存在於你的函數當中)。所以理論上至少你能夠避免使用typeof
來消除ReferenceError
的狀況。