摘要:中常常會看到這種代碼變量與的比較這種用法很有問題用來判斷變量是否被賦予了一個合理的值比如不好的寫法執行一些邏輯這段代碼中方法顯然是希望是一個數組因為我們看到的擁有和這段代碼的意圖非常明顯如果參數不是一個數組則停止接下來的操作這種寫法的問題在
js中, 常常會看到這種代碼: 變量與null的比較(這種用法很有問題), 用來判斷變量是否被賦予了一個合理的值. 比如:
const Controller = { process(items) { if(!items !== null) { // 不好的寫法 items.sort(); items.forEach(item => { // 執行一些邏輯 }); } } };
這段代碼中, process()方法顯然是希望items是一個數組, 因為我們看到的items擁有sort()和forEach(). 這段代碼的意圖非常明顯: 如果參數items不是一個數組, 則停止接下來的操作. 這種寫法的問題在于, 和null的比較不能真正的避免錯誤的發生. items的值可以是1, 也可以是字符串, 還可以是對象. 這些值都和null不相等, 進而導致process()方法一旦執行到sort()時就會報錯.
僅僅和null比較并不能提供足夠的信息來判斷后續代碼的執行是否真的安全. 好在js為我們提供了多種方法來檢測變量的真實值.
8.1 檢測原始值
typeof的基本語法是:
typeof variable; // 推薦寫法 // 或者 typeof(variable);
使用typeof來檢測這四種原值類型是非常安全的做法. 例子:
// 檢測字符串 if(typeof name === "string") { anotherName = name.substring(3); } // 檢測數字 if(typeof count === "number") { updateCount(count); } // 檢測布爾值 if(typeof found === "boolean" && found) { message("Found!"); } // 檢測undefined if(typeof MyApp === "undefined") { MyApp = { // 其他的代碼 }; }
typeof運算符的獨特之處在于, 將其用于一個為聲明的變量也不會報錯. 未定義的變量和值為undefined 的變量和值為undefined的變量通過typeof 都將返回"undefined".
最后一個原始值, null, 一般不應用于檢測語句. 正如上文提到的, 簡單地和null 比較通暢不會包含足夠的信息以判斷值得類型是否合法. 但有一個例外, 如果所期望的值真的是null, 則可以直接和null進行比較. 這時應當使用===或者!==來和null進行比較, 比如:
// 如果你需要檢測null, 則使用這種方法 const ele = document.getElementById("my-div"); if(ele !== null) { ele.className = "found"; }
運行typeof null則返回"object", 這時一種低效的判斷null的方法. 如果你需要檢測null, 則直接使用恒等運算符(===)或非恒等運算符(!==);
特別注意這里所說的typeof null => "object", 是因為null是一個空指針對象, 所以在定義變量時如果這個變量將來時對象時, 則定義為null. 在編程時杜絕使用typeof來檢測null的類型.
8.2 檢測引用值
引用值也稱作對象(object). 在js中除了原始值之外的值都是引用. 有這樣幾種內置的引用類型: Object、Array、Date和Error, 數量不多. typeof運算符在判斷這些引用類型時則顯得力不從心, 因為所有對象都會返回"object".
檢測某個引用值的類型最好的方法是使用instanceof運算符. instanceof的基本語法是:
value instanceof constructor
這里是一些例子.
// 檢測日期 if(value instanceof Date) { console.log(value.getFullYear()); } // 檢測正則表達式 if(value instanceof RegExp) { if(value.test(anotherValue)) { console.log("Mathes"); } } // 檢測Error if(value instanceof Error) { throw value; }
instanceof的一個有意思的特征是它不僅檢測構造這對象的構造器, 還檢測原型鏈. 原型鏈包含了很多信息, 包括定義對象所采用的繼承模式. 比如, 默認情況下, 每個對象都繼承Object, 因此每個對象的value instanceof Object都會返回true. 比如:
const now = new Date(); console.log(now instanceof Object); // true console.log(now instanceof Date); // true
因為這個原因, 使用value instanceof Object來判斷對象是否屬于某一個特定類型的做法并非最佳.
instanceof運算符也可以檢測自定義的類型, 比如:
function Person(name) { this.name = name; } const me = new Person("Nicholas"); console.log(me instanceof Object); // true console.log(meinstanceof Person); // true
這段示例代碼中創建了Person類型. 變量me是Person的實例, 因此me instanceof Person 是true. 上文也提到, 所有的對象都被認為是Oject的實例, 因此me instanceof Object也是true.
在js中檢測自定義類型時, 最好的做法就是使用instanceof運算符, 這也是唯一的方法. 同樣對于內置js的類型也是如此. 但是有一個嚴重的限制.
假設一個瀏覽器幀(frame A)里的一個對象被傳入到另一個幀(frame B)中. 兩個幀都定義了構造函數Person. 如果幀A的對象是幀A的Person的實例, 則如果規則成立.
// true frameAPersonInstance instanceof frameAPerson // false frameAPersonInstance instanceof frameBPerson
因為每個幀(frame)都擁有Person的一份拷貝, 它被認為是該幀(frame)中的Person的拷貝實例, 盡管兩個定義可能是完全一樣的.
這個問題不僅出現在自定義類型身上, 其他兩個非常重要的內置類型也有這個問題: 函數和數組. 對于這兩個類型來說, 一般用不著使用instanceof.
8.2.1 檢測函數
從技術上講, js中的函數是引用類型, 同樣存在Function構造函數, 每個函數都是其 實例, 比如:
function maFunc() { // 不好的寫法 console.log(myFunc instanceof Function); // true }
然而, 這個方法亦不能跨幀(frame)使用, 因為每個幀都有各自的Function構造函數. 好在typeof運算符也是可以用于函數的, 返回"function".
function myFunc() { // 好的寫法 console.log(typeof myFunc === "function"); // true }
檢測函數最好的方法是使用typeof, 因為它可以跨幀(frame)使用.
用typeof來檢測函數有一個限制. 在IE瀏覽器中, 使用typeof來檢測DOM節點(比如 document.getElementById() 中的函數都返回"object"而不是"function").
之所以出現這種怪異現象是因為瀏覽器對DOM的實現有差異. 簡言之, 這些早版本的IE并沒有將DOM實現內置的js方法, 導致內置的typeof運算符將這些函數識別為對象. 因為DOM是有明確定義的, 了解到對象成員如果存在則意味著它是一個方法, 開發者往往通過in運算符來檢測DOM的方法, 比如:
// 檢測DOM方法 if("querSelectorAll" in document) { images = document.querySelectorAll("img"); }
這段代碼檢查querySelectorAll是否定義在了document中, 如果是, 則使用這個方法. 盡管不是最理想的方法, 如果想在IE8以及更早瀏覽器中檢測DOM方是否存在, 這是最安全的做法. 在其他所有的情形中, typeof運算符是檢測js函數的最佳選擇.
8.2.2 檢測數組
js中最古老的的跨域問題之一就是在幀(frame)之間來回傳遞數組. 開發者很快發現instanceof Array在此場景中不總是返回正確的結果. 正如上文提到的, 每個幀(frame)都有各自的Array構造函數, 因此一個幀(frame)中的實例在另一個幀里不會識別. Douglas Crockford首先使用"鴨式辯型"(duck typing) 來檢測器sort()方法是否存在.
// 采用鴨式辯型的方法檢測數組 function isArray(value) { return typeof value.sort === "function"; }
這種檢測方法依賴一個事實, 即數組是唯一包含sort()方法的對象, 它也會返回true.
關于如何在js中檢測數組類型已經有很多研究了, 最終, Juriy Zaytsev(也被稱作Kangax)給出一種優雅的解決方案.
function isArray(value) { return Object.prototype.toString.call(value) === "[object Array]"; }
Kangax發現調用某個值的內置toString()方法在所有瀏覽器中都會返回標準的字符串結果. 對于數組來說, 返回字符串為"[object Array]", 也不用考慮數組實例是在哪個幀(frame)中被構造出來的. Kangax給出的解決方案很快流行起來, 并被大多數js類庫所采納.
這種方法在識別內置對象時往往十分有用, 但對于自定義對象請不要用這種方法. 比如內置JSON對象使用這種方法將返回"[object JSON]".
從那時起, ECMAScript5將Array.isArray()正式引入js. 唯一的目的就是準確的檢測一個值是否為數組. 同Kangax的函數一樣, Array.isArray()也可以檢測跨幀(frame)傳遞的值, 因此很多js類庫目前都類似的實現了這個方法.
8.3 檢測屬性
另外一種用到null(以及undefined)的場景是當檢測一個屬性是否在對象中存在時, 比如:
// 不好的寫法: 檢測假值 if(object[propertyName]) { // 一些代碼 } // 不好的寫法: 和null相比較 if(object[propertyName] != null) { // 一些代碼 } // 不好的寫法: 和undefined比較 if(object[propertyName] != undefined) { // 一些代碼 }
上面這段代碼里的每個判斷, 實際上是通過給定的名字來檢查屬性的值, 而非判斷給定的名字所指的屬性是否存在, 因為當屬性值為假值(falsy value)時結果會出錯, 比如0, "", false, null和undefined. 畢竟這些都是屬性的合法值. 比如, 如果屬性記錄了一個數字, 則這個值可以是零, 這樣的話, 上段代碼中的第一個判斷就會導致錯誤. 以此類推, 如果屬性值為null或者undefined時, 三個判斷都會導致錯誤.
判斷屬性是否存在的最好的方法是使用in運算符. in運算符僅僅會簡單的判斷屬性是否存在, 而不會去讀屬性的值, 這樣就可以避免出現本小節提到的有歧義的語句. 如果實例對象的屬性存在、或者繼承自對象的原型, in運算符都會返回true. 比如:
const obj = { count: 0, related: null }; // 好的寫法 if("count" in obj) { // do something } // 不好的寫法: 檢測假值 if(obj["count"]) { // 這里的代碼不會執行 } // 好的寫法 if("related" in object) { // 這里的代碼會執行 } // 不好的寫法: 檢測是否為null if(object["related"] != null) { // 這里的代碼不會執行 }
如果你只是想檢查實例對象的某個屬性是否存在, 則使用hasOwnProperty()方法. 所有繼承自Object的js對象都有這個方法, 如果實例中存在這個屬性則返回ture(如果這個屬性只存在于原型里, 則返回false). 需要注意的是, 在IE8以及更早版本的IE中, DOM對象并非繼承自Object, 因此也不包含這個方法. 也就是說, 你再調用DOM對象的hasOwnProperty()方法之前應當檢測其是否存在(加入你已經知道對象不是DOM, 則可以省略這一步).
// 對于所有非DOM對象來說, 這是好的寫法 if(object.hasOwnProperty("related")) { // do something } // 如果你不確定是否為DOM對象, 則這樣來寫 if("hasOwnProperty" in object && object.hasOwnProperty("related")) { // do something }
因為存在IE8以及更早版本的IE的情形, 在判斷實例對象是否存在時, 我更傾向于使用in運算符, 只有在需要判斷實例屬性時才會用到hasOwnProperty().
不管你什么時候需要檢測屬性的存在性, 請使用in運算符或者hasOwnProperty().
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/90702.html
摘要:程序是寫給人讀的只是偶爾讓計算機執行一下當你剛剛組建一個團隊時團隊中的每個人都各自有一套編程習慣畢竟每個成員都有著不同的背景有些人可能來自某個皮包公司身兼數職在公司里面什么事都做還有些人會來自不同的團隊對某種特定的做事風格情有獨鐘或恨之入骨 程序是寫給人讀的,只是偶爾讓計算機執行一下. Donald Knuth 當你剛剛組建一個團隊時,團隊中的每個人都各自有一套編程習慣.畢竟,...
摘要:由于第四章太稀松平常了于是就直接跳到第五章了這里我就草草的說一下第四章的幾個點吧在嚴格模式的應用下不推薦將用在全局作用域中相等推薦盡量使用和守則如果是在沒有別的方法來完成當前任務這時可以使用原始包裝類型不推薦創建類型時用等創建類型從這一章節 由于第四章太稀松平常了, 于是就直接跳到第五章了.這里我就草草的說一下第四章的幾個點吧 在嚴格模式的應用下 不推薦將use strict;用在全...
摘要:代碼無非是定義一些指令的集合讓計算機來執行我們常常將數據傳入計算機由指令對數據進行操作并最終產生一個結果當不得不修改數據時問題就來了任何時候你修改源代碼都會有引入的風險且值修改一些數據的值也會帶來一些不必要的風險因為數據時不應當影響指令的正 代碼無非是定義一些指令的集合讓計算機來執行. 我們常常將數據傳入計算機, 由指令對數據進行操作, 并最終產生一個結果. 當不得不修改數據時問題就來...
摘要:所有的塊語句都應當使用花括號包括花括號的對齊方式第一種風格第二種風格塊語句間隔第一種在語句名圓括號和左花括號之間沒有空格間隔第二種在左圓括號之前和右圓括號之后各添加一個空格第三種在左圓括號后和右圓括號前各添加一個空格我個人喜歡在右括號之后添 所有的塊語句都應當使用花括號, 包括: if for while do...while... try...catch...finally 3....
摘要:注釋是代碼中最常見的組成部分它們是另一種形式的文檔也是程序員最后才舍得花時間去寫的但是對于代碼的總體可維護性而言注釋是非常重要的一環打開一個沒有任何注釋的文件就好像趣味冒險但如果給你的時間有限這項任務就變成了折磨適度的添加注釋可以解釋說明代 注釋是代碼中最常見的組成部分.它們是另一種形式的文檔,也是程序員最后才舍得花時間去寫的.但是,對于代碼的總體可維護性而言,注釋是非常重要的一環.打...
閱讀 1265·2021-11-19 09:40
閱讀 3121·2021-11-02 14:47
閱讀 3080·2021-10-11 10:58
閱讀 3221·2019-08-30 15:54
閱讀 2674·2019-08-30 12:50
閱讀 1728·2019-08-29 16:54
閱讀 468·2019-08-29 15:38
閱讀 1241·2019-08-29 15:19