超越Jquery_01_isPlainObject分析与重构


Posted in Javascript onOctober 20, 2010

isPlainObject是Jquery1.4后提供的新方法,用于判断对象是否是纯粹的对象(通过 "{}" 或者 "new Object" 创建的)。

使用isPlainObject

首先我们来了解一下什么叫'纯粹的对象',简单的理解'纯粹的对象'指的就是由Object构造出来的对象。那哪些对象是由Object构造出来的呢。首当其充的肯定是由new Object()所构造出来的对象,注意:在Object后的括号里可没加任何东西。因为Object是所有'类'的根基,因此它有一些特殊的行为,如当调用new Object(3)的时候,会构造一个Number类型的对象。new Object('')会构造一个String类型的对象。然后以{}这种形式定义的对象也属于'纯粹的对象'。'{}'的实质就是new Object(),只是表现形形式不同。好,让我们来看一段代码:

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的完整版本,注释已经很详尽了,我就不多说什么了。
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,简化):
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:
function m(){}; 
m.prototype.constructor=Object; //必杀 
obj=new m; 
alert(isPlainObject(obj)); //true

再来一个同理的:
function m(){}; 
m.prototype = {}; 
obj=new m; 
alert(isPlainObject(obj)); //true

这个答案是无解的!

解答无解
本以为这个问题很好解决,结果深入后,发现这是一个无解的问题。原因如下:

function Person(){}; 
Person.prototype.constructor=Object; 
var person=new Person;

让我们来看一下person现在的状态:
超越Jquery_01_isPlainObject分析与重构
person和其构造函数Person唯一的联系就是其prototype链中的constructor属性。而在我们判断是否为'纯粹的对象'主要是依据对象实例的constructor进行的。如果我们将其指向Object,正如图中看到的那样,那么person和Person在代码上就没有关系了。也正是因为这一点,让类型的判断出现了问题。
Javascript 相关文章推荐
javascript下查找父节点的简单方法
Aug 13 Javascript
不用AJAX和IFRAME,说说真正意义上的ASP+JS无刷新技术
Sep 25 Javascript
JS 自动安装exe程序
Nov 30 Javascript
javascript下arguments,caller,callee,call,apply示例及理解
Dec 24 Javascript
jQuery contains过滤器实现精确匹配使用方法
Apr 12 Javascript
Javascript 计算字符串在localStorage中所占字节数
Oct 21 Javascript
JavaScript面向对象分层思维全面解析
Nov 22 Javascript
jQuery Ajax实现跨域请求
Jan 21 Javascript
vue-router路由简单案例介绍
Feb 21 Javascript
使用jQuery给Table动态增加行、清空table的方法
Sep 05 jQuery
react的滑动图片验证码组件的示例代码
Feb 27 Javascript
浅谈Vue为什么不能检测数组变动
Oct 14 Javascript
理解Javascript_15_作用域分配与变量访问规则,再送个闭包
Oct 20 #Javascript
理解Javascript_14_函数形式参数与arguments
Oct 20 #Javascript
理解Javascript_13_执行模型详解
Oct 20 #Javascript
用jquery与css打造个性化的单选框和复选框
Oct 20 #Javascript
Jquery插件之多图片异步上传
Oct 20 #Javascript
jquery判断checkbox(复选框)是否被选中的代码
Oct 20 #Javascript
jQuery下扩展插件和拓展函数的写法(匿名函数使用的典型例子)
Oct 20 #Javascript
You might like
虹吸壶是谁发明的?煮出来的咖啡好喝吗
2021/03/04 冲泡冲煮
ThinkPHP静态缓存简单配置和使用方法详解
2016/03/23 PHP
php 如何设置一个严格控制过期时间的session
2017/05/05 PHP
php关联数组与索引数组及其显示方法
2018/03/12 PHP
利用JS重写Cognos右键菜单的实现代码
2010/04/11 Javascript
jquery getScript动态加载JS方法改进详解
2012/11/15 Javascript
JS控制图片等比例缩放的示例代码
2013/12/24 Javascript
详谈LABJS按需动态加载js文件
2015/05/07 Javascript
ajax读取数据后使用jqchart显示图表的方法
2015/06/10 Javascript
jquery form表单获取内容以及绑定数据
2016/02/24 Javascript
window.close(); 关闭浏览器窗口js代码的总结介绍
2016/07/14 Javascript
js实现增加数字显示的环形进度条效果
2017/02/05 Javascript
整理关于Bootstrap警示框的慕课笔记
2017/03/29 Javascript
微信小程序上滑加载下拉刷新(onscrollLower)分批加载数据(二)
2017/05/11 Javascript
详解如何使用webpack在vue项目中写jsx语法
2017/11/08 Javascript
基于vue 实现表单中password输入的显示与隐藏功能
2019/07/19 Javascript
JavaScript实现单图片上传并预览功能
2019/09/30 Javascript
vue指令v-html使用过滤器filters功能实例
2019/10/25 Javascript
jQuery模仿ToDoList实现简单的待办事项列表
2019/12/30 jQuery
[15:41]教你分分钟做大人——灰烬之灵
2015/03/11 DOTA
在Python3中使用asyncio库进行快速数据抓取的教程
2015/04/02 Python
django2.0扩展用户字段示例
2019/02/13 Python
详解Python列表赋值复制深拷贝及5种浅拷贝
2019/05/15 Python
Python实现二叉树前序、中序、后序及层次遍历示例代码
2019/05/18 Python
美国名表在线商城:Ashford(支持中文)
2019/09/24 全球购物
向全球直邮输送天然健康产品:iHerb.com
2020/05/03 全球购物
如何配置、使用和清除Smarty缓存
2015/12/23 面试题
道德模范先进事迹
2014/02/14 职场文书
小学学校评估方案
2014/06/08 职场文书
实习单位指导教师评语
2014/12/30 职场文书
2015学校年度工作总结
2015/05/11 职场文书
2016年大学校运会广播稿件
2015/12/21 职场文书
springboot使用Redis作缓存使用入门教程
2021/07/25 Redis
vue使用watch监听属性变化
2022/04/30 Vue.js
css3手动实现pc端横向滚动
2022/06/21 HTML / CSS
Win10玩csgo闪退如何解决?Win10玩csgo闪退的解决方法
2022/07/23 数码科技