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