导读:本期聚焦于星宫一花创作的《如何正确使用jQuery.isPlainObject()判断对象是否由{}或new Object()创建?》,敬请观看详情。一个常见的误解是,只要对象的 typeof 结果为 object 就把它当作纯对象处理,这会导致数组、Date 实例甚至 DOM 节点被错误地纳入普通对象逻辑。jQuery.isPlainObject 方法存在的意义,正是精确识别那些由对象字面量或 new Object 生成的对象。该方法会先排除非对象和 toString 标记不符的值,再沿着原型链检查构造器,只有原型直接指向 Object.prototype 或原型为 null 的对象才会返回 true。跨 iframe 场景下它比 instanceof Object 更可靠,因为内部比较的是构造函数的字符串签名而非引用。本文深入分析这一判断逻辑的源码实现,并与 Object.prototype.toString.call、instanceof 等常见方式做对比,帮助开发者在深拷贝、配置合并、API 参数校验等场景正确区分纯对象与其它引用类型。

判断一个对象是不是“纯对象”看似简单,实际却藏着不少边界情况。很多JavaScript开发者习惯用 typeof obj === 'object' 来区分对象,但数组、null、Date实例、正则表达式、DOM节点等都会返回 object,无法满足精确判断的需求。jQuery.isPlainObject() 正是为了回答“这个对象是否由对象字面量{}或 new Object() 创建”而生。它在jQuery工具方法中地位突出,常被用在 $.extend、深拷贝以及插件参数解析等场景。理解它的判断标准,能有效避免类型误判引发的数据污染和逻辑分支错误。

如何正确使用jQuery.isPlainObject()判断对象是否由{}或new Object()创建?

纯对象与其它对象类型有什么区别

JavaScript中的对象并不都是“普通对象”。从原型角度划分,通过 {}new Object() 创建的对象,其原型直接指向 Object.prototype;而数组、函数、正则、日期等内置类型虽然本质也是对象,但它们的原型链上多出了 Array.prototypeFunction.prototypeRegExp.prototypeDate.prototype 等中间层。这意味着它们虽然继承自 Object,却具备各自类型的特殊行为,不能简单视为纯数据容器。

再看自定义构造函数:function Person(name){this.name = name;} 之后 new Person('Tom') 产生的实例虽然可以保存数据,但其原型是 Person.prototype,而不是 Object.prototype。此时如果使用通用对象处理逻辑,可能会遗漏原型上的自定义方法,或者在进行浅合并时错误复制枚举属性之外的内容。纯对象通常只用来承载键值对,不包含额外原型方法,因此很多库会要求传入纯对象以保证行为可预期。

另一个容易混淆的场景是 Object.create(null)。它创建的对象没有原型链,连 hasOwnProperty 等方法都不存在,但jQuery在新版本中将其视为纯对象,因为其本质仍然是数据容器,且更纯粹。这一处理与只判断原型是否直接为 Object.prototype 的方案不同,后续会详细说明。

jQuery.isPlainObject()的实现原理与内部逻辑

jQuery 3.x中的 isPlainObject 源码非常精炼。首先调用 Object.prototype.toString 判断传入值的类型标签是否为 [object Object],如果连这个基本条件都不满足,直接返回 false。这一步可以快速排除 null、数组、函数、字符串等。接着通过内部 getProto 方法获取对象的原型。如果原型为 null,说明是 Object.create(null) 的结果,新版本将其判定为纯对象并返回 true。若原型存在,则继续检查该原型上是否有自己的 constructor 属性,并取出 constructor 函数。最后比较该构造函数的字符串表示是否与全局 Object 函数的字符串表示完全一致。只有完全一致才返回 true。

// jQuery.isPlainObject 简化源码(jQuery 3.x)
function isPlainObject(obj) {
    var proto, Ctor;

    // 使用 toString 检测类型,排除 null、非对象等
    if (!obj || Object.prototype.toString.call(obj) !== "[object Object]") {
        return false;
    }

    proto = Object.getPrototypeOf(obj);

    // 没有原型的对象,如 Object.create(null)
    if (!proto) {
        return true;
    }

    // 原型上必须有自有 constructor 属性
    Ctor = Object.prototype.hasOwnProperty.call(proto, "constructor") && proto.constructor;
    return typeof Ctor === "function" &&
           Function.prototype.toString.call(Ctor) === Function.prototype.toString.call(Object);
}

// 测试
console.log(isPlainObject({}));              // true
console.log(isPlainObject(new Object()));    // true
console.log(isPlainObject(Object.create(null))); // true
console.log(isPlainObject(new Date()));      // false
console.log(isPlainObject([]));              // false
console.log(isPlainObject(function() {}));   // false

这里比较构造函数字符串的原因是跨 iframe 或跨窗口环境下,不同 realm 中的 Object 构造函数并不是同一个引用,直接使用 constructor === Object 会导致来自另一个窗口的对象被误判为 false。字符串签名比较则绕开了引用不一致的问题,让判断在主流浏览器中保持稳定。当然,这种做法也无法百分之百覆盖恶意构造的伪造对象,但对于常规开发已经足够。

旧版本 jQuery(如 1.x、2.x)对该方法有过不同实现,有的使用 Object.getPrototypeOf 并与 Object.prototype 做比较,也有的尝试访问 obj.constructor 来判断,但都存在一些边界缺陷。例如 Object.create(null) 曾经返回 false,跨 iframe 对象也会出现偏差。jQuery 3.x 的改进使行为更符合直觉。

实际开发中的应用场景与常见误区

isPlainObject 最常见的应用之一是 $.extend 方法的内部判断。$.extend 在做深浅拷贝时,需要确定目标对象的类型。如果传入的是数组、DOM节点或自定义类实例,复制逻辑会完全不同。例如 $.extend(true, {}, {items: [1,2]}) 时,数组会被当作引用处理还是深拷贝对象,直接影响结果。jQuery 通过 isPlainObject 识别纯对象后,才能决定是否递归合并其键值。

插件开发中也经常使用。一个函数可能接受一组配置项,有时用户直接传配置对象,有时传字符串或 DOM 元素,这时可以先调用 isPlainObject 判断配置项,若不是则构造默认对象再填充。这种防御式代码能减少运行时错误。例如:function init(options){ if(!$.isPlainObject(options)){ options = {}; } this.config = $.extend({}, defaults, options); }

// 插件参数校验
function setupWidget(options) {
    // 不是纯对象时重置为空对象,避免后续合并出错
    if (!jQuery.isPlainObject(options)) {
        options = {};
    }
    var settings = jQuery.extend({}, {
        color: "blue",
        size: "medium"
    }, options);
    return settings;
}

console.log(setupWidget({ color: "red" }));       // 正常合并
console.log(setupWidget([1, 2, 3]));              // 数组被忽略,使用默认值
console.log(setupWidget(new Person("Tom")));      // 自定义实例被忽略

常见误区之一是认为 Object.prototype.toString.call(obj) === "[object Object]" 就已经足够判断纯对象。这个判断只能说明 obj 是普通对象类型,但无法区分自定义构造函数的实例。例如 new Person() 的 toString 结果也是 [object Object],但它的原型并不是 Object.prototype。因此仅靠 toString 标签会造成误判。

另一个误区是将 instanceof Object 当作万能检测。instanceof 检查的是原型链上是否出现过 Object.prototype,数组、函数、Date 实例都会返回 true。而且跨 iframe 时,来自其他窗口的对象使用当前窗口的 Object 做 instanceof 判断会返回 false,产生假阴性。isPlainObject 通过字符串比较绕开了这一问题,虽然也不是完全跨库通用,但在 jQuery 内部足够可靠。

与现代JavaScript原生方案的对比与替代

如果你不希望依赖 jQuery,也可以用原生方法实现类似 isPlainObject 的功能。在同一个 realm 内,最简单的判断是:

function isPlainObjectNative(obj) {
    if (obj === null || typeof obj !== "object") {
        return false;
    }
    var proto = Object.getPrototypeOf(obj);
    return proto === Object.prototype || proto === null;
}

console.log(isPlainObjectNative({}));                 // true
console.log(isPlainObjectNative(Object.create(null))); // true
console.log(isPlainObjectNative([]));                  // false
console.log(isPlainObjectNative(new Date()));          // false
console.log(isPlainObjectNative(Object.create({})));   // false

这个方法在大部分单窗口应用中可以良好工作,但它没有处理跨 iframe 场景。若你的页面嵌入了多个 iframe,并且需要在父窗口判断来自 iframe 的对象,上述函数会因为 Object.prototype 引用不同而返回 false。此时可以借鉴 jQuery 的思路,比较构造函数的 toString 结果。不过如果你的应用不涉及跨窗口对象,使用原生方法更轻量,也能避免引入整个 jQuery 库。

此外,lodash 也提供了 _.isPlainObject 方法,其实现思路与 jQuery 类似,同样使用 Object.prototype.toString 和原型链检查,并处理了跨 realm 情况。如果你在项目中已经使用 lodash,可以直接调用它,不必自己编写。但了解其原理有助于在代码审查或迁移时做出合理选择。

需要特别注意的是,ES6 的 class 语法创建的实例也不是纯对象,因为 class 本质上仍是构造函数,实例的原型指向类的 prototype。例如 class Foo {}; new Foo() 经过 isPlainObject 判断会返回 false。这与直觉一致:它承载了类逻辑,不应被普通对象工具处理。而 Object.create(Object.prototype) 创建的对象虽然原型直接是 Object.prototype,会被判定为 true,这也没有问题,因为行为与 {} 一致。

使用isPlainObject时需要注意的性能与兼容性细节

isPlainObject 内部需要调用 Object.prototype.toString、获取原型、可能还包含函数字符串化对比。与简单的 typeof 判断相比,开销略高,但在绝大多数业务代码中不会成为瓶颈。如果你在循环或高频路径中大量调用该方法,可以考虑缓存结果或预先做类型白名单。将 isPlainObject 用于深层递归拷贝时,每一层对象都会触发一次判断,这在处理超大对象图时可能累积成可感知的耗时,但通常仍然远小于深拷贝本身的开销。

兼容性方面,jQuery 3.x 已经放弃了 IE8 及更早版本,因此可以安全使用 Object.getPrototypeOfFunction.prototype.toString 等 ES5 方法。如果你仍需兼容旧环境,可以使用 jQuery 1.x,但需要接受其对 Object.create(null) 返回 false 的差异。在项目升级 jQuery 版本时,应回归测试所有依赖 isPlainObject 的代码,尤其是那些传入无原型对象或跨 iframe 对象的场景。

最后强调,尽管 isPlainObject 能精确识别纯对象,但并不意味着其它对象类型就应该被限制使用。数组、Map、Set、类实例等都有其适用场景。工具方法的目的是在需要“纯数据容器”语义时提供可靠判断,而不是鼓励将所有数据处理都塞进普通对象。合理选择数据结构,配合准确的类型检测,才能写出稳健的 JavaScript 代码。

jQuery.isPlainObject纯对象判断对象原型修改时间:2026-08-27 07:38:09

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。