超越Jquery_01_isPlainObject分析与重构
isPlainObject是Jquery1.4后提供的新方法,用于判断对象是否是纯粹的对象(通过 "{}" 或者 "new Object" 创建的)。
使用isPlainObject
首先我们来了解一下什么叫'纯粹的对象',简单的理解'纯粹的对象'指的就是由Object构造出来的对象。那哪些对象是由Object构造出来的呢。首当其充的肯定是由new Object()所构造出来的对象,注意:在Object后的括号里可没加任何东西。因为Object是所有'类'的根基,因此它有一些特殊的行为,如当调用new Object(3)的时候,会构造一个Number类型的对象。new Object('')会构造一个String类型的对象。然后以{}这种形式定义的对象也属于'纯粹的对象'。'{}'的实质就是new Object(),只是表现形形式不同。好,让我们来看一段代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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的完整版本,注释已经很详尽了,我就不多说什么了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 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] //结论:obj.nodeType || obj.setInterval主要是针对于IE浏览器进行判断 //注: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 // 除去自定义对象和内置对象的判断,如function Person(){} var p = new Person();String,Number if ( obj.constructor //有constructor属性 && !hasOwnProperty.call(obj, "constructor") //并且constructor这个属性必须是在原型链中进行定义的 && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")//并且原型中有isPrototypeOf方法,一般只有Object的原型中才有这个方法 ) { return false; } // Own properties are enumerated firstly, so to speed up, // if last one is own, then all properties are own. //针对于复杂类结构,如有继承... /* //一个简单的测试 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 ); } |
提出问题
个人感觉这个实现比较复杂,而且有BUG。
简单的BUG,history,location,navigator,screen可以顺序通过 isPlainObject的检测返回true.
来看一个我的解决方案(修改BUG,简化):
1 2 3 4 5 6 7 8 | 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,而且是一个无解的BUG:
1 2 3 4 | function m(){}; m.prototype.constructor=Object; //必杀 obj=new m; alert(isPlainObject(obj)); //true |
再来一个同理的:
1 2 3 4 | function m(){}; m.prototype = {}; obj=new m; alert(isPlainObject(obj)); //true |
这个答案是无解的!
解答无解
本以为这个问题很好解决,结果深入后,发现这是一个无解的问题。原因如下:
1 2 3 | function Person(){}; Person.prototype.constructor=Object; var person=new Person; |
让我们来看一下person现在的状态:
person和其构造函数Person唯一的联系就是其prototype链中的constructor属性。而在我们判断是否为'纯粹的对象'主要是依据对象实例的constructor进行的。如果我们将其指向Object,正如图中看到的那样,那么person和Person在代码上就没有关系了。也正是因为这一点,让类型的判断出现了问题。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· 用 DeepSeek 给对象做个网站,她一定感动坏了
· DeepSeek+PageAssist实现本地大模型联网
· 手把手教你更优雅的享受 DeepSeek
· Java轻量级代码工程
· 从 14 秒到 1 秒:MySQL DDL 性能优化实战