超越Jquery_01_isPlainObject分析與重構(gòu)
isPlainObject是Jquery1.4后提供的新方法,用于判斷對(duì)象是否是純粹的對(duì)象(通過(guò) "{}" 或者 "new Object" 創(chuàng)建的)。
使用isPlainObject
首先我們來(lái)了解一下什么叫'純粹的對(duì)象',簡(jiǎn)單的理解'純粹的對(duì)象'指的就是由Object構(gòu)造出來(lái)的對(duì)象。那哪些對(duì)象是由Object構(gòu)造出來(lái)的呢。首當(dāng)其充的肯定是由new Object()所構(gòu)造出來(lái)的對(duì)象,注意:在Object后的括號(hào)里可沒(méi)加任何東西。因?yàn)镺bject是所有'類(lèi)'的根基,因此它有一些特殊的行為,如當(dāng)調(diào)用new Object(3)的時(shí)候,會(huì)構(gòu)造一個(gè)Number類(lèi)型的對(duì)象。new Object('')會(huì)構(gòu)造一個(gè)String類(lèi)型的對(duì)象。然后以{}這種形式定義的對(duì)象也屬于'純粹的對(duì)象'。'{}'的實(shí)質(zhì)就是new Object(),只是表現(xiàn)形形式不同。好,讓我們來(lái)看一段代碼:
var objStr = new Object('');
alert(objStr.constructor);//String
alert(isPlainObject(objStr));//false
var objNum = new Object(3);
alert(objNum.constructor);//Number
alert(isPlainObject(objNum));//false
function Person(){}
var person = new Person();
alert(isPlainObject(person));//false
var obj01 = new Object();
obj01.name = '笨蛋的座右銘';
alert(isPlainObject(obj01));//true
alert(isPlainObject({name:'笨蛋的座右銘'}));//true
isPlainObject源碼分析
以下代碼為Jquery中的isPlainObject的完整版本,注釋已經(jīng)很詳盡了,我就不多說(shuō)什么了。
var toString = Object.prototype.toString,
hasOwnProperty = Object.prototype.hasOwnProperty;
function isPlainObject( obj ) {
// Must be an Object.
// Because of IE, we also have to check the presence of the constructor property.
//Make sure that DOM nodes and window objects don't pass through, as well
//windows objects:toString.call(window):IE [object Object] FF [object Window] chrome [window global] safari [object DOMWindow]
//DOM nodes:toString.call(#div01):IE [object Object] FF [object Window] chrome [object global] safari [object DOMWindow]
//結(jié)論:obj.nodeType || obj.setInterval主要是針對(duì)于IE瀏覽器進(jìn)行判斷
//注:history,location,navigator,screen的setInterval為undefined
if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) {
return false;
}
// Not own constructor property must be Object
// 除去自定義對(duì)象和內(nèi)置對(duì)象的判斷,如function Person(){} var p = new Person();String,Number
if ( obj.constructor //有constructor屬性
&& !hasOwnProperty.call(obj, "constructor") //并且constructor這個(gè)屬性必須是在原型鏈中進(jìn)行定義的
&& !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")//并且原型中有isPrototypeOf方法,一般只有Object的原型中才有這個(gè)方法
) {
return false;
}
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
//針對(duì)于復(fù)雜類(lèi)結(jié)構(gòu),如有繼承...
/*
//一個(gè)簡(jiǎn)單的測(cè)試
function Animal(name){
}
function Person(name,age){
Animal.call(this,name);
this.age =age;
}
var p = new Person('jxl',20);
for(key in p){
alert(hasOwnProperty.call( p, key ))//true , false
}
*/
var key;
for ( key in obj ) {}
return key === undefined || hasOwnProperty.call( obj, key );
}
提出問(wèn)題
個(gè)人感覺(jué)這個(gè)實(shí)現(xiàn)比較復(fù)雜,而且有BUG。
簡(jiǎn)單的BUG,history,location,navigator,screen可以順序通過(guò) isPlainObject的檢測(cè)返回true.
來(lái)看一個(gè)我的解決方案(修改BUG,簡(jiǎn)化):
function isPlainObject(obj){
if(obj&&Object.prototype.toString.call(obj)==="[object Object]"&&obj.constructor===Object &&!hasOwnProperty.call(obj, "constructor")){
var key;
for ( key in obj ) {}
return key === undefined || hasOwnProperty.call( obj, key );
}
return false;
}
還有BUG,而且是一個(gè)無(wú)解的BUG:
function m(){};
m.prototype.constructor=Object; //必殺
obj=new m;
alert(isPlainObject(obj)); //true
再來(lái)一個(gè)同理的:
function m(){};
m.prototype = {};
obj=new m;
alert(isPlainObject(obj)); //true
這個(gè)答案是無(wú)解的!
解答無(wú)解
本以為這個(gè)問(wèn)題很好解決,結(jié)果深入后,發(fā)現(xiàn)這是一個(gè)無(wú)解的問(wèn)題。原因如下:
function Person(){};
Person.prototype.constructor=Object;
var person=new Person;
讓我們來(lái)看一下person現(xiàn)在的狀態(tài):

person和其構(gòu)造函數(shù)Person唯一的聯(lián)系就是其prototype鏈中的constructor屬性。而在我們判斷是否為'純粹的對(duì)象'主要是依據(jù)對(duì)象實(shí)例的constructor進(jìn)行的。如果我們將其指向Object,正如圖中看到的那樣,那么person和Person在代碼上就沒(méi)有關(guān)系了。也正是因?yàn)檫@一點(diǎn),讓類(lèi)型的判斷出現(xiàn)了問(wèn)題。
相關(guān)文章
詳解JavaScript如何利用異步解密回調(diào)地獄
為了更好地處理這些異步操作,JavaScript?引入了異步編程的概念,這篇文章主要來(lái)和大家詳細(xì)聊聊JavaScript中異步的相關(guān)應(yīng)用,希望對(duì)大家有所幫助2024-02-02
js es6系列教程 - 基于new.target屬性與es5改造es6的類(lèi)語(yǔ)法
下面小編就為大家?guī)?lái)一篇js es6系列教程 - 基于new.target屬性與es5改造es6的類(lèi)語(yǔ)法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09
每個(gè) JavaScript 工程師都應(yīng)懂的33個(gè)概念
這個(gè)項(xiàng)目是為了幫助開(kāi)發(fā)者掌握 JavaScript 概念而創(chuàng)立的。它不是必備,但在未來(lái)學(xué)習(xí)( JavaScript )中,可以作為一篇指南,需要的朋友可以參考下2018-10-10
JS將網(wǎng)址url轉(zhuǎn)化為JSON格式的方法
這篇文章主要介紹了JS將網(wǎng)址url轉(zhuǎn)化為JSON格式的方法,需要的朋友可以參考下2018-07-07
教你用typescript類(lèi)型來(lái)推算斐波那契
斐波那契是自然界與社會(huì)中存在的一種數(shù)學(xué)規(guī)律,下面這篇文章主要給大家介紹了關(guān)于如何用typescript類(lèi)型來(lái)推算斐波那契的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-01-01
Javascript將字符串日期格式化為yyyy-mm-dd的方法
日期格式化相信對(duì)于大家來(lái)說(shuō)再熟悉不過(guò),最近工作中自己利用Javascript就寫(xiě)了一個(gè),現(xiàn)在將實(shí)現(xiàn)的代碼分享給大家,希望對(duì)有需要的朋友們能有所幫助,感興趣的朋友們下面來(lái)一起看看吧。2016-10-10

