摘要:專(zhuān)題系列預(yù)計(jì)寫(xiě)二十篇左右,主要研究日常開(kāi)發(fā)中一些功能點(diǎn)的實(shí)現(xiàn),比如防抖節(jié)流去重類(lèi)型判斷拷貝最值扁平柯里遞歸亂序排序等,特點(diǎn)是研究和的實(shí)現(xiàn)方式。
前言JavaScript專(zhuān)題系列第五篇,講解更加復(fù)雜的類(lèi)型判斷,比如 plainObject、空對(duì)象、類(lèi)數(shù)組對(duì)象、Window對(duì)象、DOM 元素等
在上篇《JavaScript專(zhuān)題之類(lèi)型判斷(上)》中,我們抄襲 jQuery 寫(xiě)了一個(gè) type 函數(shù),可以檢測(cè)出常見(jiàn)的數(shù)據(jù)類(lèi)型,然而在開(kāi)發(fā)中還有更加復(fù)雜的判斷,比如 plainObject、空對(duì)象、Window 對(duì)象等,這一篇就讓我們接著抄襲 jQuery 去看一下這些類(lèi)型的判斷。
plainObjectplainObject 來(lái)自于 jQuery,可以翻譯成純粹的對(duì)象,所謂"純粹的對(duì)象",就是該對(duì)象是通過(guò) "{}" 或 "new Object" 創(chuàng)建的,該對(duì)象含有零個(gè)或者多個(gè)鍵值對(duì)。
之所以要判斷是不是 plainObject,是為了跟其他的 JavaScript對(duì)象如 null,數(shù)組,宿主對(duì)象(documents)等作區(qū)分,因?yàn)檫@些用 typeof 都會(huì)返回object。
jQuery提供了 isPlainObject 方法進(jìn)行判斷,先讓我們看看使用的效果:
function Person(name) { this.name = name; } console.log($.isPlainObject({})) // true console.log($.isPlainObject(new Object)) // true console.log($.isPlainObject(Object.create(null))); // true console.log($.isPlainObject(Object.assign({a: 1}, {b: 2}))); // true console.log($.isPlainObject(new Person("yayu"))); // false console.log($.isPlainObject(Object.create({}))); // false
由此我們可以看到,除了 {} 和 new Object 創(chuàng)建的之外,jQuery 認(rèn)為一個(gè)沒(méi)有原型的對(duì)象也是一個(gè)純粹的對(duì)象。
實(shí)際上隨著 jQuery 版本的提升,isPlainObject 的實(shí)現(xiàn)也在變化,我們今天講的是 3.0 版本下的 isPlainObject,我們直接看源碼:
// 上節(jié)中寫(xiě) type 函數(shù)時(shí),用來(lái)存放 toString 映射結(jié)果的對(duì)象 var class2type = {}; // 相當(dāng)于 Object.prototype.toString var toString = class2type.toString; // 相當(dāng)于 Object.prototype.hasOwnProperty var hasOwn = class2type.hasOwnProperty; function isPlainObject(obj) { var proto, Ctor; // 排除掉明顯不是obj的以及一些宿主對(duì)象如Window if (!obj || toString.call(obj) !== "[object Object]") { return false; } /** * getPrototypeOf es5 方法,獲取 obj 的原型 * 以 new Object 創(chuàng)建的對(duì)象為例的話(huà) * obj.__proto__ === Object.prototype */ proto = Object.getPrototypeOf(obj); // 沒(méi)有原型的對(duì)象是純粹的,Object.create(null) 就在這里返回 true if (!proto) { return true; } /** * 以下判斷通過(guò) new Object 方式創(chuàng)建的對(duì)象 * 判斷 proto 是否有 constructor 屬性,如果有就讓 Ctor 的值為 proto.constructor * 如果是 Object 函數(shù)創(chuàng)建的對(duì)象,Ctor 在這里就等于 Object 構(gòu)造函數(shù) */ Ctor = hasOwn.call(proto, "constructor") && proto.constructor; // 在這里判斷 Ctor 構(gòu)造函數(shù)是不是 Object 構(gòu)造函數(shù),用于區(qū)分自定義構(gòu)造函數(shù)和 Object 構(gòu)造函數(shù) return typeof Ctor === "function" && hasOwn.toString.call(Ctor) === hasOwn.toString.call(Object); }
注意:我們判斷 Ctor 構(gòu)造函數(shù)是不是 Object 構(gòu)造函數(shù),用的是 hasOwn.toString.call(Ctor),這個(gè)方法可不是 Object.prototype.toString,不信我們?cè)诤瘮?shù)里加上下面這兩句話(huà):
console.log(hasOwn.toString.call(Ctor)); // function Object() { [native code] } console.log(Object.prototype.toString.call(Ctor)); // [object Function]
發(fā)現(xiàn)返回的值并不一樣,這是因?yàn)?hasOwn.toString 調(diào)用的其實(shí)是 Function.prototype.toString,畢竟 hasOwnProperty 可是一個(gè)函數(shù)!
而且 Function 對(duì)象覆蓋了從 Object 繼承來(lái)的 Object.prototype.toString 方法。函數(shù)的 toString 方法會(huì)返回一個(gè)表示函數(shù)源代碼的字符串。具體來(lái)說(shuō),包括 function關(guān)鍵字,形參列表,大括號(hào),以及函數(shù)體中的內(nèi)容。
EmptyObjectjQuery提供了 isEmptyObject 方法來(lái)判斷是否是空對(duì)象,代碼簡(jiǎn)單,我們直接看源碼:
function isEmptyObject( obj ) { var name; for ( name in obj ) { return false; } return true; }
其實(shí)所謂的 isEmptyObject 就是判斷是否有屬性,for 循環(huán)一旦執(zhí)行,就說(shuō)明有屬性,有屬性就會(huì)返回 false。
但是根據(jù)這個(gè)源碼我們可以看出isEmptyObject實(shí)際上判斷的并不僅僅是空對(duì)象。
舉個(gè)栗子:
console.log(isEmptyObject({})); // true console.log(isEmptyObject([])); // true console.log(isEmptyObject(null)); // true console.log(isEmptyObject(undefined)); // true console.log(isEmptyObject(1)); // true console.log(isEmptyObject("")); // true console.log(isEmptyObject(true)); // true
以上都會(huì)返回 true。
但是既然 jQuery 是這樣寫(xiě),可能是因?yàn)榭紤]到實(shí)際開(kāi)發(fā)中 isEmptyObject 用來(lái)判斷 {} 和 {a: 1} 是足夠的吧。如果真的是只判斷 {},完全可以結(jié)合上篇寫(xiě)的 type 函數(shù)篩選掉不適合的情況。
Window對(duì)象Window 對(duì)象作為客戶(hù)端 JavaScript 的全局對(duì)象,它有一個(gè) window 屬性指向自身,這點(diǎn)在《JavaScript深入之變量對(duì)象》中講到過(guò)。我們可以利用這個(gè)特性判斷是否是 Window 對(duì)象。
function isWindow( obj ) { return obj != null && obj === obj.window; }isArrayLike
isArrayLike,看名字可能會(huì)讓我們覺(jué)得這是判斷類(lèi)數(shù)組對(duì)象的,其實(shí)不僅僅是這樣,jQuery 實(shí)現(xiàn)的 isArrayLike,數(shù)組和類(lèi)數(shù)組都會(huì)返回 true。
因?yàn)樵创a比較簡(jiǎn)單,我們直接看源碼:
function isArrayLike(obj) { // obj 必須有 length屬性 var length = !!obj && "length" in obj && obj.length; var typeRes = type(obj); // 排除掉函數(shù)和 Window 對(duì)象 if (typeRes === "function" || isWindow(obj)) { return false; } return typeRes === "array" || length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj; }
重點(diǎn)分析 return 這一行,使用了或語(yǔ)句,只要一個(gè)為 true,結(jié)果就返回 true。
所以如果 isArrayLike 返回true,至少要滿(mǎn)足三個(gè)條件之一:
是數(shù)組
長(zhǎng)度為 0
lengths 屬性是大于 0 的數(shù)組,并且obj[length - 1]必須存在
第一個(gè)就不說(shuō)了,看第二個(gè),為什么長(zhǎng)度為 0 就可以直接判斷為 true 呢?
那我們寫(xiě)個(gè)對(duì)象:
var obj = {a: 1, b: 2, length: 0}
isArrayLike 函數(shù)就會(huì)返回 true,那這個(gè)合理嗎?
回答合不合理之前,我們先看一個(gè)例子:
function a(){ console.log(isArrayLike(arguments)) } a();
如果我們?nèi)サ鬺ength === 0 這個(gè)判斷,就會(huì)打印 false,然而我們都知道 arguments 是一個(gè)類(lèi)數(shù)組對(duì)象,這里是應(yīng)該返回 true 的。
所以是不是為了放過(guò)空的 arguments 時(shí)也放過(guò)了一些存在爭(zhēng)議的對(duì)象呢?
第三個(gè)條件:length 是數(shù)字,并且 length > 0 且最后一個(gè)元素存在。
為什么僅僅要求最后一個(gè)元素存在呢?
讓我們先想下數(shù)組是不是可以這樣寫(xiě):
var arr = [,,3]
當(dāng)我們寫(xiě)一個(gè)對(duì)應(yīng)的類(lèi)數(shù)組對(duì)象就是:
var arrLike = { 2: 3, length: 3 }
也就是說(shuō)當(dāng)我們?cè)跀?shù)組中用逗號(hào)直接跳過(guò)的時(shí)候,我們認(rèn)為該元素是不存在的,類(lèi)數(shù)組對(duì)象中也就不用寫(xiě)這個(gè)元素,但是最后一個(gè)元素是一定要寫(xiě)的,要不然 length 的長(zhǎng)度就不會(huì)是最后一個(gè)元素的 key 值加 1。比如數(shù)組可以這樣寫(xiě)
var arr = [1,,]; console.log(arr.length) // 2
但是類(lèi)數(shù)組對(duì)象就只能寫(xiě)成:
var arrLike = { 0: 1, length: 1 }
所以符合條件的類(lèi)數(shù)組對(duì)象是一定存在最后一個(gè)元素的!
這就是滿(mǎn)足 isArrayLike 的三個(gè)條件,其實(shí)除了 jQuery 之外,很多庫(kù)都有對(duì) isArrayLike 的實(shí)現(xiàn),比如 underscore:
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; var isArrayLike = function(collection) { var length = getLength(collection); return typeof length == "number" && length >= 0 && length <= MAX_ARRAY_INDEX; };isElement
isElement 判斷是不是 DOM 元素。
isElement = function(obj) { return !!(obj && obj.nodeType === 1); };結(jié)語(yǔ)
這一篇我們介紹了 jQuery 的 isPlainObject、isEmptyObject、isWindow、isArrayLike、以及 underscore 的 isElement 實(shí)現(xiàn)。我們可以看到,即使是 jQuery 這樣優(yōu)秀的庫(kù),一些方法的實(shí)現(xiàn)也并不是非常完美和嚴(yán)密的,但是最后為什么這么做,其實(shí)也是一種權(quán)衡,權(quán)衡所失與所得,正如玉伯在《從 JavaScript 數(shù)組去重談性能優(yōu)化》中講到:
所有這些點(diǎn),都必須腳踏實(shí)地在具體應(yīng)用場(chǎng)景下去分析、去選擇,要讓場(chǎng)景說(shuō)話(huà)。
專(zhuān)題系列JavaScript專(zhuān)題系列目錄地址:https://github.com/mqyqingfeng/Blog。
JavaScript專(zhuān)題系列預(yù)計(jì)寫(xiě)二十篇左右,主要研究日常開(kāi)發(fā)中一些功能點(diǎn)的實(shí)現(xiàn),比如防抖、節(jié)流、去重、類(lèi)型判斷、拷貝、最值、扁平、柯里、遞歸、亂序、排序等,特點(diǎn)是研(chao)究(xi) underscore 和 jQuery 的實(shí)現(xiàn)方式。
如果有錯(cuò)誤或者不嚴(yán)謹(jǐn)?shù)牡胤剑?qǐng)務(wù)必給予指正,十分感謝。如果喜歡或者有所啟發(fā),歡迎 star,對(duì)作者也是一種鼓勵(lì)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/83845.html
摘要:專(zhuān)題系列共計(jì)篇,主要研究日常開(kāi)發(fā)中一些功能點(diǎn)的實(shí)現(xiàn),比如防抖節(jié)流去重類(lèi)型判斷拷貝最值扁平柯里遞歸亂序排序等,特點(diǎn)是研究專(zhuān)題之函數(shù)組合專(zhuān)題系列第十六篇,講解函數(shù)組合,并且使用柯里化和函數(shù)組合實(shí)現(xiàn)模式需求我們需要寫(xiě)一個(gè)函數(shù),輸入,返回。 JavaScript 專(zhuān)題之從零實(shí)現(xiàn) jQuery 的 extend JavaScritp 專(zhuān)題系列第七篇,講解如何從零實(shí)現(xiàn)一個(gè) jQuery 的 ext...
摘要:寫(xiě)在前面專(zhuān)題系列是我寫(xiě)的第二個(gè)系列,第一個(gè)系列是深入系列。專(zhuān)題系列自月日發(fā)布第一篇文章,到月日發(fā)布最后一篇,感謝各位朋友的收藏點(diǎn)贊,鼓勵(lì)指正。 寫(xiě)在前面 JavaScript 專(zhuān)題系列是我寫(xiě)的第二個(gè)系列,第一個(gè)系列是 JavaScript 深入系列。 JavaScript 專(zhuān)題系列共計(jì) 20 篇,主要研究日常開(kāi)發(fā)中一些功能點(diǎn)的實(shí)現(xiàn),比如防抖、節(jié)流、去重、類(lèi)型判斷、拷貝、最值、扁平、柯里...
摘要:將元素作為對(duì)象的鍵,默認(rèn)鍵對(duì)應(yīng)的值為如果對(duì)象中沒(méi)有這個(gè)鍵,則將這個(gè)元素放入結(jié)果數(shù)組中去。 前言 數(shù)組去重在日常開(kāi)發(fā)中的使用頻率還是較高的,也是網(wǎng)上隨便一抓一大把的話(huà)題,所以,我寫(xiě)這篇文章目的在于歸納和總結(jié),既然很多人都在提的數(shù)組去重,自己到底了解多少呢。又或者是如果自己在開(kāi)發(fā)中遇到了去重的需求,自己能想到更好的解決方案嗎。 這次我們來(lái)理一理怎么做數(shù)組去重才能做得最合適,既要考慮兼容性,...
摘要:專(zhuān)題系列第四篇,講解類(lèi)型判斷的各種方法,并且跟著寫(xiě)一個(gè)函數(shù)。返回值為表示操作數(shù)類(lèi)型的一個(gè)字符串??紤]到實(shí)際情況下并不會(huì)檢測(cè)和,所以去掉這兩個(gè)類(lèi)型的檢測(cè)。 JavaScript專(zhuān)題系列第四篇,講解類(lèi)型判斷的各種方法,并且跟著 jQuery 寫(xiě)一個(gè) type 函數(shù)。 前言 類(lèi)型判斷在 web 開(kāi)發(fā)中有非常廣泛的應(yīng)用,簡(jiǎn)單的有判斷數(shù)字還是字符串,進(jìn)階一點(diǎn)的有判斷數(shù)組還是對(duì)象,再進(jìn)階一點(diǎn)的有判...
摘要:不過(guò)的實(shí)現(xiàn)中,多了很多細(xì)節(jié)上的判斷,比如第一個(gè)參數(shù)是否是布爾值,是否是一個(gè)對(duì)象,不傳參數(shù)時(shí)的默認(rèn)值等。 JavaScritp 專(zhuān)題系列第七篇,講解如何從零實(shí)現(xiàn)一個(gè) jQuery 的 extend 函數(shù) 前言 jQuery 的 extend 是 jQuery 中應(yīng)用非常多的一個(gè)函數(shù),今天我們一邊看 jQuery 的 extend 的特性,一邊實(shí)現(xiàn)一個(gè) extend! extend 基本用...
閱讀 1103·2021-11-24 09:39
閱讀 1338·2021-11-18 13:18
閱讀 2511·2021-11-15 11:38
閱讀 1854·2021-09-26 09:47
閱讀 1658·2021-09-22 15:09
閱讀 1653·2021-09-03 10:29
閱讀 1544·2019-08-29 17:28
閱讀 2976·2019-08-29 16:30