在JavaScript中typeof的用途介紹
JavaScript 中的 typeof 其實(shí)非常復(fù)雜,它可以用來(lái)做很多事情,但同時(shí)也有很多怪異的表現(xiàn)。
本文列舉出了它的多個(gè)用法,而且還指出了存在的問(wèn)題以及解決辦法。
閱讀本文的前提是,你現(xiàn)在應(yīng)該已經(jīng)知道原始值和對(duì)象值的區(qū)別了。
檢查一個(gè)變量是否存在,是否有值
typeof在兩種情況下會(huì)返回 "undefined":
1.變量沒(méi)有被聲明
2.變量的值是 undefined
例如:
> typeof undeclaredVariable === "undefined"
true
> var declaredVariable;
> typeof declaredVariable
'undefined'
> typeof undefined
'undefined'
還有其他辦法檢測(cè)某個(gè)值是否是 undefined:
> var value = undefined;
> value === undefined
true
但這種方法如果使用在一個(gè)未聲明的變量上的時(shí)候,就會(huì)拋出異常,因?yàn)橹挥?typeof 才可以正常檢測(cè)未聲明的變量的同時(shí)還不報(bào)錯(cuò):
> undeclaredVariable === undefined
ReferenceError: undeclaredVariable is not defined
注意:未初始化的變量,沒(méi)有被傳入?yún)?shù)的形參,不存在的屬性,都不會(huì)出現(xiàn)上面的問(wèn)題,因?yàn)樗鼈兛偸强稍L問(wèn)的,值總是 undefined:
> var declaredVariable;
> declaredVariable === undefined
true
> (function (x) { return x === undefined }())
true
> ({}).foo === undefined
true
譯者注:因此,如果想檢測(cè)一個(gè)可能沒(méi)有被聲明的全局變量是否存在,也可以使用if(window.maybeUndeclaredVariable){}。
問(wèn)題:typeof 在完成這樣的任務(wù)時(shí)顯得很繁雜.
解決辦法:這樣的操作不是很常見(jiàn),所以有人覺(jué)的沒(méi)必要再找更好的解決辦法了。 不過(guò)也許有人會(huì)提出一個(gè)專門的操作符:
> defined undeclaredVariable
false
> var declaredVariable;
> defined declaredVariable
false
或者,也許有人還需要一個(gè)檢測(cè)變量是否被聲明的操作符:
> declared undeclaredVariable
false
> var declaredVariable;
> declared declaredVariable
true
譯者注:在 perl 里,上面的 defined 操作符相當(dāng)于 defined(),上面的 declared 操作符相當(dāng)于 exists()。
判斷一個(gè)值不等于 undefined 也不等于 null
問(wèn)題:如果你想檢測(cè)一個(gè)值是否被定義過(guò)(值不是 undefined 也不是 null),那么你就遇到了 typeof 最有名的一個(gè)怪異表現(xiàn)(被認(rèn)為是一個(gè) bug):typeof null 返回了 "object":
> typeof null
'object'
譯者注:這只能說(shuō)是最初的 JavaScript 實(shí)現(xiàn)的 bug,而現(xiàn)在標(biāo)準(zhǔn)就是這樣規(guī)范的。V8 曾經(jīng)修正并實(shí)現(xiàn)過(guò) typeof null === "null",但最終證明不可行。http://wiki.ecmascript.org/doku.php?id=harmony:typeof_null。
(譯注:typeof 在操作 null 時(shí)會(huì)返回 "object",這是 JavaScript 語(yǔ)言本身的 bug。不幸的是,這個(gè) bug 永遠(yuǎn)不可能被修復(fù)了,因?yàn)樘嘁延械拇a已經(jīng)依賴了這樣的表現(xiàn)。但是 null 到底是不是 對(duì)象呢?stackoverflow 有關(guān)于這個(gè)問(wèn)題的討論:http://stackoverflow.com/questions/801032/null-object-in-javascript/7968470#7968470@justjavac)
解決辦法:不要使用 typeof 來(lái)做這項(xiàng)任務(wù),用下面這樣的函數(shù)來(lái)代替:
function isDefined(x) {
return x !== null && x !== undefined;
}
另一個(gè)可能性是引入一個(gè) “默認(rèn)值運(yùn)算符”,在 myValue 未定義的情況下,下面的表達(dá)式會(huì)返回 defaultValue:
myValue ?? defaultValue
上面的表達(dá)式等價(jià)于:
(myValue !== undefined && myValue !== null) ? myValue : defaultValue
又或者:
myValue ??= defaultValue
其實(shí)是下面這條語(yǔ)句的簡(jiǎn)化:
myValue = myValue ?? defaultValue
當(dāng)你訪問(wèn)一個(gè)嵌套的屬性時(shí),比如 bar,你或許會(huì)需要這個(gè)運(yùn)算符的幫助:
obj.foo.bar
如果 obj 或者 obj.foo 是未定義的,上面的表達(dá)式會(huì)拋出異常。 一個(gè)運(yùn)算符 .?? 可以讓上面的表達(dá)式在遍歷一層一層的屬性時(shí),返回第一個(gè)遇到的值為 undefined 或 null 的屬性:
obj.??foo.??bar
上面的表達(dá)式等價(jià)于:
(obj === undefined || obj === null) ? obj
: (obj.foo === undefined || obj.foo === null) ? obj.foo
: obj.foo.bar
區(qū)分對(duì)象值和原始值
下面的函數(shù)用來(lái)檢測(cè) x 是否是一個(gè)對(duì)象值:
function isObject(x) {
return (typeof x === "function"
|| (typeof x === "object" && x !== null));
}
問(wèn)題:上面的檢測(cè)比較復(fù)雜,是因?yàn)?typeof 把函數(shù)和對(duì)象看成是不同的類型,而且 typeof null 返回 "object".
解決辦法:下面的方法也經(jīng)常用于檢測(cè)對(duì)象值:
function isObject2(x) {
return x === Object(x);
}
警告:你也許認(rèn)為這里可以使用 instanceof Object 來(lái)檢測(cè),但是 instanceof 是通過(guò)使用使用一個(gè)對(duì)象的原型來(lái)判斷實(shí)例關(guān)系的,那么沒(méi)有原型的對(duì)象怎么辦呢:
> var obj = Object.create(null);
> Object.getPrototypeOf(obj)
null
obj 確實(shí)是一個(gè)對(duì)象,但它不是任何值的實(shí)例:
> typeof obj
'object'
> obj instanceof Object
false
在實(shí)際中,你可能很少遇到這樣的對(duì)象,但它的確存在,而且有它的用途。
譯者注:Object.prototype 就是唯一的一個(gè)內(nèi)置的,沒(méi)有原型的對(duì)象。
>Object.getPrototypeOf(Object.prototype)
null
>typeof Object.prototype
'object'
>Object.prototype instanceof Object
false
原始值的類型是什么?
typeof 是最好的用來(lái)查看某個(gè)原始值的類型的方式。
> typeof "abc"
'string'
> typeof undefined
'undefined'
問(wèn)題:你必須知道 typeof null 的怪異表現(xiàn)。
> typeof null // 要小心!
'object'
解決辦法:下面的函數(shù)可以修復(fù)這個(gè)問(wèn)題(只針對(duì)這個(gè)用例)。
function getPrimitiveTypeName(x) {
var typeName = typeof x;
switch(typeName) {
case "undefined":
case "boolean":
case "number":
case "string":
return typeName;
case "object":
if (x === null) {
return "null";
}
default: // 前面的判斷都沒(méi)通過(guò)
throw new TypeError("參數(shù)不是一個(gè)原始值: "+x);
}
}
更好的解決辦法:實(shí)現(xiàn)一個(gè)函數(shù) getTypeName(),除了可以返回原始值的的類型,還可以返回對(duì)象值的內(nèi)部 [[Class]] 屬性。 這里講了如何實(shí)現(xiàn)這個(gè)函數(shù)(譯者注:jQuery 中的 $.type 就是這樣的實(shí)現(xiàn))
某個(gè)值是否是函數(shù)
typeof 可以用來(lái)檢測(cè)一個(gè)值是否是函數(shù)。
> typeof function () {}
'function'
> typeof Object.prototype.toString
'function'
原則上說(shuō),instanceof Function 也可以進(jìn)行這種需求的檢測(cè)。 乍一看,貌似寫法還更加優(yōu)雅。 但是,瀏覽器有一個(gè)怪癖:每一個(gè)框架和窗口都有它自己的全局變量。 因此,如果你將某個(gè)框架中的對(duì)象傳到另一個(gè)框架中,instanceof 就不能正常工作了,因?yàn)檫@兩個(gè)框架有著不同的構(gòu)造函數(shù)。 這就是為什么 ECMAScript5 中會(huì)有Array.isArray() 方法的原因。 如果有一個(gè)能夠跨框架的,用于檢查一個(gè)對(duì)象是否是給定的構(gòu)造函數(shù)的實(shí)例的方法的話,那會(huì)很好。 上述的 getTypeName() 是一個(gè)可用的變通方法,但也許還有一個(gè)更根本的解決方案。
綜述
下面提到的,應(yīng)該是目前 JavaScript 中最迫切需要的,可以代替一些 typeof 目前職責(zé)的功能特性:
•isDefined() (比如 Object.isDefined()): 可以作為一個(gè)函數(shù)或者一個(gè)運(yùn)算符
•isObject()
•getTypeName()
•能夠跨框架的,檢測(cè)一個(gè)對(duì)象是否是指定的構(gòu)造函數(shù)的實(shí)例的機(jī)制
檢查某個(gè)變量是否已經(jīng)被聲明這樣的需求,可能沒(méi)那么必要有自己的運(yùn)算符。
相關(guān)文章
JavaScript對(duì)象學(xué)習(xí)經(jīng)驗(yàn)整理
主要包括對(duì)象的創(chuàng)建、對(duì)象屬性的設(shè)置和查詢、對(duì)象方法等等,整理如下,感興趣的朋友可以參考下2013-10-10
詳解JavaScript實(shí)現(xiàn)異步Ajax
本文詳細(xì)講解了JavaScript實(shí)現(xiàn)異步Ajax的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05
Javascript中自動(dòng)切換焦點(diǎn)實(shí)現(xiàn)代碼
本文提供Javascript中自動(dòng)切換焦點(diǎn)實(shí)例代碼,需要了解的朋友可以參考下2012-12-12
JavaScript對(duì)象創(chuàng)建及繼承原理實(shí)例解剖
本文將用實(shí)例講解一下JavaScript對(duì)象創(chuàng)建及繼承原理:JavaScript中的繼承是使用原型鏈的機(jī)制,對(duì)象創(chuàng)建使用Function構(gòu)造器,感興趣的朋友可以詳細(xì)了解下本文,或許可以幫助到你2013-02-02
淺談Javascript中勻速運(yùn)動(dòng)的停止條件
這篇文章主要給我們探討了Javascript中勻速運(yùn)動(dòng)的停止條件的原理及其與緩沖運(yùn)動(dòng)的區(qū)別,需要的朋友可以參考下2014-12-12

