在JavaScript中typeof的用途介紹

來源:互聯網
上載者:User

JavaScript 中的 typeof 其實非常複雜,它可以用來做很多事情,但同時也有很多怪異的表現。

本文列舉出了它的多個用法,而且還指出了存在的問題以及解決辦法。

閱讀本文的前提是,你現在應該已經知道原始值和對象值的區別了。

檢查一個變數是否存在,是否有值
typeof在兩種情況下會返回 "undefined":

1.變數沒有被聲明

2.變數的值是 undefined

例如:

複製代碼 代碼如下:> typeof undeclaredVariable === "undefined"
true

> var declaredVariable;
> typeof declaredVariable
'undefined'

> typeof undefined
'undefined'

還有其他辦法檢測某個值是否是 undefined:

複製代碼 代碼如下:> var value = undefined;
> value === undefined
true

但這種方法如果使用在一個未聲明的變數上的時候,就會拋出異常,因為只有 typeof 才可以正常檢測未聲明的變數的同時還不報錯:

複製代碼 代碼如下:> undeclaredVariable === undefined
ReferenceError: undeclaredVariable is not defined

注意:未初始化的變數,沒有被傳入參數的形參,不存在的屬性,都不會出現上面的問題,因為它們總是可訪問的,值總是 undefined:

複製代碼 代碼如下:> var declaredVariable;
> declaredVariable === undefined
true

> (function (x) { return x === undefined }())
true

> ({}).foo === undefined
true

譯者註:因此,如果想檢測一個可能沒有被聲明的全域變數是否存在,也可以使用if(window.maybeUndeclaredVariable){}。

問題:typeof 在完成這樣的任務時顯得很繁雜.

解決辦法:這樣的操作不是很常見,所以有人覺的沒必要再找更好的解決辦法了。 不過也許有人會提出一個專門的操作符:

複製代碼 代碼如下:> defined undeclaredVariable
false

> var declaredVariable;
> defined declaredVariable
false

或者,也許有人還需要一個檢測變數是否被聲明的操作符:

複製代碼 代碼如下:> declared undeclaredVariable
false

> var declaredVariable;
> declared declaredVariable
true

譯者註:在 perl 裡,上面的 defined 操作符相當於 defined(),上面的 declared 操作符相當於 exists()。

判斷一個值不等於 undefined 也不等於 null
問題:如果你想檢測一個值是否被定義過(值不是 undefined 也不是 null),那麼你就遇到了 typeof 最有名的一個怪異表現(被認為是一個 bug):typeof null 返回了 "object":

複製代碼 代碼如下:> typeof null
'object'

譯者註:這隻能說是最初的 JavaScript 實現的 bug,而現在標準就是這樣規範的。V8 曾經修正並實現過 typeof null === "null",但最終證明不可行。http://wiki.ecmascript.org/doku.php?id=harmony:typeof_null。

(譯註:typeof 在操作 null 時會返回 "object",這是 JavaScript 語言本身的 bug。不幸的是,這個 bug 永遠不可能被修複了,因為太多已有的代碼已經依賴了這樣的表現。但是 null 到底是不是 對象呢?stackoverflow 有關於這個問題的討論:http://stackoverflow.com/questions/801032/null-object-in-javascript/7968470#7968470@justjavac)

解決辦法:不要使用 typeof 來做這項任務,用下面這樣的函數來代替:

複製代碼 代碼如下:function isDefined(x) {
return x !== null && x !== undefined;
}

另一個可能性是引入一個 “預設值運算子”,在 myValue 未定義的情況下,下面的運算式會返回 defaultValue:

複製代碼 代碼如下:myValue ?? defaultValue

上面的運算式等價於:

複製代碼 代碼如下:(myValue !== undefined && myValue !== null) ? myValue : defaultValue

又或者:

複製代碼 代碼如下:myValue ??= defaultValue

其實是下面這條語句的簡化:

複製代碼 代碼如下:myValue = myValue ?? defaultValue

當你訪問一個嵌套的屬性時,比如 bar,你或許會需要這個運算子的協助:

複製代碼 代碼如下:obj.foo.bar

如果 obj 或者 obj.foo 是未定義的,上面的運算式會拋出異常。 一個運算子 .?? 可以讓上面的運算式在遍曆一層一層的屬性時,返回第一個遇到的值為 undefined 或 null 的屬性:

複製代碼 代碼如下:obj.??foo.??bar

上面的運算式等價於:

複製代碼 代碼如下:(obj === undefined || obj === null) ? obj
: (obj.foo === undefined || obj.foo === null) ? obj.foo
: obj.foo.bar

區分對象值和原始值

下面的函數用來檢測 x 是否是一個對象值:

複製代碼 代碼如下:function isObject(x) {
return (typeof x === "function"
|| (typeof x === "object" && x !== null));
}

問題:上面的檢測比較複雜,是因為 typeof 把函數和對象看成是不同的類型,而且 typeof null 返回 "object".

解決辦法:下面的方法也經常用於檢測對象值:

複製代碼 代碼如下:function isObject2(x) {
return x === Object(x);
}

警告:你也許認為這裡可以使用 instanceof Object 來檢測,但是 instanceof 是通過使用使用一個對象的原型來判斷執行個體關係的,那麼沒有原型的對象怎麼辦呢:

複製代碼 代碼如下:> var obj = Object.create(null);
> Object.getPrototypeOf(obj)
null

obj 確實是一個對象,但它不是任何值的執行個體:

複製代碼 代碼如下:> typeof obj
'object'
> obj instanceof Object
false

在實際中,你可能很少遇到這樣的對象,但它的確存在,而且有它的用途。

譯者註:Object.prototype 就是唯一的一個內建的,沒有原型的對象。

複製代碼 代碼如下:>Object.getPrototypeOf(Object.prototype)
null
>typeof Object.prototype
'object'
>Object.prototype instanceof Object
false

原始值的類型是什麼?
typeof 是最好的用來查看某個原始值的類型的方式。

複製代碼 代碼如下:> typeof "abc"
'string'
> typeof undefined
'undefined'

問題:你必須知道 typeof null 的怪異表現。

複製代碼 代碼如下:> typeof null // 要小心!
'object'

解決辦法:下面的函數可以修複這個問題(只針對這個用例)。

複製代碼 代碼如下: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: // 前面的判斷都沒通過
throw new TypeError("參數不是一個原始值: "+x);
}
}

更好的解決辦法:實現一個函數 getTypeName(),除了可以返回原始值的的類型,還可以返回對象值的內部 [[Class]] 屬性。 這裡講了如何?這個函數(譯者註:jQuery 中的 $.type 就是這樣的實現)

某個值是否是函數
typeof 可以用來檢測一個值是否是函數。

複製代碼 代碼如下:> typeof function () {}
'function'
> typeof Object.prototype.toString
'function'

原則上說,instanceof Function 也可以進行這種需求的檢測。 乍一看,貌似寫法還更加優雅。 但是,瀏覽器有一個怪癖:每一個架構和視窗都有它自己的全域變數。 因此,如果你將某個架構中的對象傳到另一個架構中,instanceof 就不能正常工作了,因為這兩個架構有著不同的建構函式。 這就是為什麼 ECMAScript5 中會有Array.isArray() 方法的原因。 如果有一個能夠跨架構的,用於檢查一個對象是否是給定的建構函式的執行個體的方法的話,那會很好。 上述的 getTypeName() 是一個可用的變通方法,但也許還有一個更根本的解決方案。

綜述
下面提到的,應該是目前 JavaScript 中最迫切需要的,可以代替一些 typeof 目前職責的功能特性:

•isDefined() (比如 Object.isDefined()): 可以作為一個函數或者一個運算子

•isObject()

•getTypeName()

•能夠跨架構的,檢測一個對象是否是指定的建構函式的執行個體的機制

檢查某個變數是否已經被聲明這樣的需求,可能沒那麼必要有自己的運算子。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.