在 JavaScript 中,我們常常會看到這樣的代碼:變量與 null 的比較(這種用法很有問題),用來判斷變量是否被賦予了一個合理的值。比如:
var Controller = {process: function(items) {if (items !== null) { // 不好的寫法items.sort();items.forEach(function(item) {// 執行一些邏輯});}}}在這段代碼中, process() 方法顯然希望 items 是一個數組,因為我們看到 items 擁有 sort() 和 forEach() 。這段代碼的意圖非常明顯:如果參數 items 不是一個組數,則停止接下來的操作。這種寫法的問題在于,和 null 的比較并不能真正避免錯誤的發生。 items 的值可以是1,也可以是是字符串,甚至可以是任意對象。這些值都和 null 不相等,進而會導致 process() 方法一旦執行到 sort() 時就會出錯。
僅僅和 null 比較并不能提供足夠的信息來判斷后續代碼的執行是否真的安全。好在 JavaScript 為我們提供了很多種方法來檢測變量的真實值。
檢測原始值
在 JavaScript 中有5種原始類型(也稱為簡單數據類型): String 、 Number 、 Boolean 、 Undefined 和 Null 。如果你希望一個值是 String 、 Number 、 Boolean 或 Undefined ,最佳選擇是使用 typeof 運算符,它會返回一個表示類型的字符串。
對于字符串, typeof 返回 "string" 。
對于數字, typeof 返回 "number" 。
對于布爾值, typeof 返回 "boolean" 。
對于undefined, typeof 返回 "undefined" 。
typeof 的基本語法是: typeof variable ,你還可以這樣用: typeof(variable) ,盡管這是合法的 JavaScript 語法,這種用法讓 typeof 看起來像一個函數而非運算符。鑒于此,我們更推薦無括號的寫法。
使用 typeof 來檢測這4種原始類型是非常安全的做法。來看下面這些例子。
// 檢測"String"if (typeof name === "string") {anotherName = name.substring(3);}// 檢測"Number"if (typeof count === "number") {updateCount(count);}// 檢測"Boolean"if (typeof found === "boolean" && found) {message("Found!");}// 檢測"Undefined"if (typeof MyApp === "undefined") {MyApp = {// 其他代碼};}typeof 運算符的獨特之處在于,將其用于一個未聲明的變量也不會報錯。未定義的變量和值為 undefined 的變量通過 typeof 都將返回 "undefined" 。
最后一個原始類型 null ,通過 typeof 將返回 "object" ,這看上去很怪異,被認為是標準規范的嚴重 bug,因此在編程時要 杜絕使用 typeof 來檢測 null 的類型 。
console.log(typeof null); // "object"
簡單地和 null 進行比較通常不會包含足夠的信息以判斷值的類型是否合法,所以 null 一般不應用于檢測語句。
但有一個例外,如果所期望的值真的是 null ,則可以直接和 null 進行比較。例如:
// 如果你需要檢測 null,則使用這種方法var element = document.getElementById("my-div");if (element !== null) {element.className = "found";}如果 DOM 元素不存在,則通過 document.getElementById() 得到的值為 null 。這個方法要么返回一個節點,要么返回 null 。由于這時 null 是可預見的一種輸出,則可以用恒等運算符 === 或非恒等運算符 !== 來檢測返回結果。
typeof 運算符的返回值除了上述提到的 string 、 number 、 boolean 、 undefined 和 object 之外,還有 function 。從技術的角度來講,函數在 JavaScript 中也是對象,不是一種數據類型。然而,函數也確實有一些特殊的屬性,因此通過 typeof 運算符來區分函數和其他對象是有必要的。這一特性將在后面 檢測函數 中用到。
檢測引用值
在 JavaScript 中除了原始值之外的都是引用值(也稱為對象),常用的引用類型有: Object 、 Array 、 Date 和 RegExp ,這些引用類型都是 JavaScript 的內置對象。 typeof 運算符在判斷這些引用類型時全都返回 "object" 。
console.log(typeof {}); // "object"console.log(typeof []); // "object"console.log(typeof new Date()); // "object"console.log(typeof new RegExp()); // "object"檢測某個引用值類型的最好方法是使用 instanceof 運算符, instanceof 的基本語法是:
value instanceof constructor// 檢測日期if (value instanceof Date) {console.log(value.getFullYear);}// 檢測 Errorif (value instanceof Error) {throw value;}// 檢測正則表達式if (value instanceof RegExp) {if (value.test(anotherValue)) {console.log("Matches");}}instanceof 的一個有意思的特性是它不僅檢測構造這個對象的構造器,還檢測原型鏈。原型鏈包含了很多信息,包括定義對象所采用的繼承模式。比如,默認情況下,每個對象都繼承自 Object ,因此每個對象的 value instanceof Object 都會返回 ture 。比如:
var now = new Date();console.log(now instanceof Object); // tureconsole.log(now instanceof Date); // tureinstanceof 運算符也可以檢測自定義的類型,比如:function Person(name){this.name = name;}var me = new Person("Nicholas");console.log(me instanceof Object); // tureconsole.log(me instanceof Person); // ture這段示例代碼中創建了 Person 類型。變量 me 是 Person 的實例,因此 me instanceof Person 是 true 。上文也提到,所有的對象都被認為是 Object 的實例,因此 me instanceof Object 也是 ture 。
在 JavaScript 中檢測 內置類型 和 自定義類型 時,最好的做法就是使用 instanceof 運算符,這也是唯一的方法。
但有一個嚴重的限制,假設兩個瀏覽器幀(frame)里都有構造函數 Person ,幀A中的 Person 實例 frameAPersonInstance 傳入到幀B中,則會有如下結果:
console.log(frameAPersonInstance instanceof frameAPerson) // ture
console.log(frameAPersonInstance instanceof frameBPerson) // false
盡管兩個 Person 的定義是完全一樣的,但在不同幀(frame)里,他們被認為是不同類型。有兩個非常重要的內置類型也有這個問題: Array 和 Function ,所以檢測它們一般不使用 instanceof 。
檢測函數
從技術上講,JavaScript 中的函數是引用類型,同樣存在 Function 構造函數,每個函數都是其實例,比如:
function myFunc() {}// 不好的寫法console.log(myFunc instanceof Function); // true然而,這個方法亦不能跨幀(frame)使用,因為每個幀都有各自的 Function 構造函數,好在 typeof 運算符也是可以用于函數的,返回 "function" 。
function myFunc() {}// 好的寫法console.log(typeof myFunc === "function"); // true檢測函數最好的方法是使用 typeof ,因為他可以跨幀(frame)使用。
用 typeof 來檢測函數有一個限制。在 IE 8 和更早版本的 IE 瀏覽器中,使用 typeof 來檢測 DOM 節點中的函數都返回 "object" 而不是 "function" 。比如:
// IE8 及更早版本的IEconsole.log(typeof document.createElement); // "object"console.log(typeof document.getElementById); // "object"console.log(typeof document.getElementByTagName); // "object"
之所以出現這種怪異的現象是因為瀏覽器對 DOM 的實現有差異。簡言之,這些早版本的 IE 并沒有將 DOM 實現為內置的 JavaScript 方法,導致內置 typeof 運算符將這些函數識別為對象。因為 DOM 是有明確定義的,了解到對象成員如果存在則意味著它是一個方法,開發者往往通過 in 運算符來檢測 DOM 的方法,比如:
// 檢測 DOM 方法if ("querySelectorAll" in document) {var images = document.querySelectorAll("img");}這段代碼檢查 querySelectorAll 是否定義在 document 中,如果是,則使用這個方法。盡管不是最理想的方法,如果想在 IE 8 及更早瀏覽器中檢測 DOM 方法是否存在,這是最安全的做法。在其他所有的情形中, typeof 運算符是檢測 JavaScript 函數的最佳選擇。
檢測數組
JavaScript 中最古老的跨域問題之一就是在幀(frame)之間來回傳遞數組。開發者很快發現 instanceof Array 在此場景中不能返回正確的結果。正如上文提到的,每個幀都有各自的 Array 構造函數,因此一個幀中的實例在另外一個幀里不會被識別。
關于如何在 JavaScript 中檢測數組類型已經有狠多研究了,最終 Kangax 給出了一種優雅的解決方案:
function isArray(value) {return Object.prototype.toString.call(value) === "[object Array]";}Kangax 發現調用某個值的內置 toString() 方法在所有瀏覽器中都會返回標準的字符串結果。對于數組來說,返回的字符串為 "[object Array]" ,也不用考慮數組實例實在哪個幀(frame)中被構造出來的。這種方法在識別內置對象時往往十分有用,但對于自定義對象請不要用這種方法。
ECMAScript5 將 Array.isArray() 正式引入 JavaScript。唯一的目的就是準確地檢測一個值是否為數組。同 Kangax 的函數一樣, Array.isArray() 也可以檢測跨幀(frame)傳遞的值,因此很多 JavaScript 類庫目前都類似地實現了這個方法。
function isArray(value) {if (typeof Array.isArray === "function") {return Array.isArray(value);} else {return Object.prototype.toString.call(value) === "[object Array]";}}IE 9+、FireFox 4+、Safari 5+、Opera 10.5+、Chrome 都實現了 Array.isArray() 方法。
檢測屬性
另外一種用到 null (以及 undefined )的場景是當檢測一個屬性是否在對象中存在時,比如:
// 不好的寫法:檢測假值if (object[propertyName]) {// 一些代碼}// 不好的寫法:和null相比較if (object[propertyName] != null) {// 一些代碼}// 不好的寫法:和undefined相比較if (object[propertyName] != undefined) {// 一些代碼}上面這段代碼里的每個判斷,實際上是通過給定的名字來檢查屬性的值,而并非判斷給定的名字所指的屬性是否存在。在第一個判斷中,當屬性值為假值時結果會出錯,比如: 0 、 ""(空字符串) 、 false 、 null 和 undefined ,畢竟這些都是屬性的合法值。
判斷屬性是否存在的最好的方法是使用 in 運算符。 in 運算符僅僅會簡單地判斷屬性是否存在,而不去讀屬性的值,如果實例對象的屬性存在、或者繼承自對象的原型, in 運算符都會返回 true 。比如:
var object = {count: 0,related: null};// 好的寫法if ("count" in object) {// 這里的代碼會執行}// 不好的寫法:檢測假值if (object["count"]) {// 這里的代碼不會執行}// 好的寫法if ("related" in object) {// 這里的代碼會執行}// 不好的寫法,檢測是否為if (object["related"] != null) {// 這里的代碼不會執行}如果你只想檢查實例對象的某個屬性是否存在,則使用 hasOwnProperty() 方法。所有繼承自 Object 的 JavaScript 對象都有這個方法,如果實例中存在這個屬性則返回 true (如果這個屬性只存在于原型里,則返回 false )。需要注意的是,在 IE 8 以及更早版本的 IE 中,DOM 對象并非繼承自 Object,因此也不包含這個方法。也就是說,你在調用 DOM 對象的 hasOwnProperty() 方法之前應當先檢測其是否存在。
// 對于所有非 DOM 對象來說,這是好的寫法if (object.hasOwnProperty("related")) {// 執行這里的代碼會}// 如果你不確定是否為 DOM 對象,則這樣來寫if ("hasOwnProperty" in object && object.hasOwnProperty("related")) {// 執行這里的代碼會}因為存在 IE 8 以及更早版本的 IE 的情形,在判斷實例對象的屬性是否存在時,我更傾向于使用 in 運算符,只有在需要判斷實例屬性時才會用到 hasOwnProperty() 。
不管你什么時候需要檢測屬性的存在性,請使用 in 運算符或者 hasOwnProperty() 。這樣做可以避免很多 bug。
以上所述是小編給大家介紹的JavaScript檢測原始值、引用值、屬性,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!
新聞熱點
疑難解答