在编写单元测试或者对比两份配置数据时,经常需要判断两个对象的内容是否完全一致。JavaScript中的===运算符只能比较两个变量是否指向同一个引用,一旦涉及嵌套结构,简单的等号比较就失效了。Node.js在核心模块util中提供了isDeepStrictEqual方法,专门用于深度比较两个值,无需安装任何第三方依赖,是处理这类问题的官方方案。本文将详细介绍这个方法的用法、比较规则以及与其他常见方案的对比。

一、基本用法与调用方式
util.isDeepStrictEqual是Node.js核心模块util导出的一个函数,从Node.js 9版本开始提供,使用前只需引入util模块即可。它接收两个参数,返回一个布尔值,表示两个值经过深度比较后是否严格相等。
const util = require('util');
const a = { name: 'node', tags: ['js', 'backend'] };
const b = { name: 'node', tags: ['js', 'backend'] };
console.log(a === b); // false,引用不同
console.log(util.isDeepStrictEqual(a, b)); // true,内容深度相等上面的例子体现了它最常见的使用场景:两个对象虽然引用不同,但结构完全一致。对于单元测试来说,这一点非常关键。如果你不想引入断言库,可以直接用它来验证函数的返回结果是否符合预期。
方法名中的Strict意味着比较规则是严格的,类似于===与==的区别。例如数字1和字符串'1'在宽松模式下可能被视为相等,而在isDeepStrictEqual中会被判定为不相等。
const util = require('util');
console.log(util.isDeepStrictEqual(1, 1)); // true
console.log(util.isDeepStrictEqual(1, '1')); // false,类型不同
console.log(util.isDeepStrictEqual(0, false)); // false,严格模式
console.log(util.isDeepStrictEqual(null, undefined)); // false这种严格性保证了比较结果的确定性,避免了隐式类型转换带来的隐患,非常适合用于自动化测试等对准确性要求高的场合。
二、深度比较规则详解
isDeepStrictEqual内部实现了一套相当完善的比较算法,对各种JavaScript内置类型都有明确的处理规则,理解这些规则有助于避免误用。
首先是嵌套结构的递归比较。对象和数组会被逐层展开比较,只要任何一层的键名、键值或数组元素不一致,整体就返回false。同时,比较双方的原型也会被检查,也就是说字面量对象与通过构造函数创建的实例,即使属性完全相同,只要原型链不同就会被判定为不相等。
const util = require('util');
function Person(name) { this.name = name; }
const p1 = new Person('tom');
const p2 = { name: 'tom' };
// 原型不同,即使属性一致也不相等
console.log(util.isDeepStrictEqual(p1, p2)); // false
// 原型相同时属性一致即相等
console.log(util.isDeepStrictEqual(new Person('tom'), new Person('tom'))); // true其次是一些特殊值的处理。NaN与NaN的比较结果为true,这一点与Object.is一致,而+0与-0则被视为相等。对于Date对象,比较的是时间戳;对于RegExp对象,比较的是正则表达式源字符串和标志位;对于Error对象,比较的是message和name属性。
const util = require('util');
console.log(util.isDeepStrictEqual(NaN, NaN)); // true
console.log(util.isDeepStrictEqual(new Date('2024-01-01'), new Date('2024-01-01'))); // true
console.log(util.isDeepStrictEqual(/ab/g, /ab/g)); // true
console.log(util.isDeepStrictEqual(/ab/g, /ab/i)); // false,标志位不同
console.log(util.isDeepStrictEqual(new Error('x'), new Error('x'))); // true另外,isDeepStrictEqual还专门处理了WeakMap、WeakSet这类不可枚举的结构,两个WeakMap实例即使内容相同也会返回false,因为其内部数据无法被遍历比较。而Buffer、TypedArray等二进制结构则会逐字节比较,这对Node.js开发者来说尤其实用。
const util = require('util');
console.log(util.isDeepStrictEqual(Buffer.from('abc'), Buffer.from('abc'))); // true
console.log(util.isDeepStrictEqual(Buffer.from('abc'), Buffer.from('abd'))); // false三、与assert.deepStrictEqual及其他方案的对比
不少人对util.isDeepStrictEqual和assert.deepStrictEqual的关系感到困惑。实际上assert.deepStrictEqual在内部正是调用了util.isDeepStrictEqual,两者的比较规则完全一致。区别在于断言版本在比较失败时会抛出带有详细差异信息的AssertionError,而util版本只返回布尔值。在单元测试中用断言更直观,在业务逻辑中做条件判断则用util版本更合适。
const assert = require('assert');
const util = require('util');
// 断言失败会抛错,并输出差异说明
assert.deepStrictEqual({ a: 1 }, { a: 2 });
// AssertionError: Expected inputs to be strictly deep-equal
// util版本适合业务判断
if (util.isDeepStrictEqual(oldConfig, newConfig)) {
console.log('配置无变化,跳过重新加载');
}再来看其他常见方案。手写递归比较虽然可控,但要正确处理循环引用、原型、Symbol键、各种内置对象,代码量和出错概率都不小。用JSON.stringify做序列化对比则存在明显缺陷:undefined属性会被丢弃,日期会变成字符串,NaN会变成null,函数和Symbol直接丢失,而且键的顺序还会影响结果。
console.log(JSON.stringify({ a: 1, b: undefined })); // '{"a":1}'
console.log(JSON.stringify({ b: undefined, a: 1 })); // '{"b":undefined...}' 键顺序影响
// util版本不受键顺序影响
console.log(util.isDeepStrictEqual({ a: 1, b: undefined }, { b: undefined, a: 1 })); // true第三方库中Lodash的isEqual也很流行,它支持循环引用并且可以自定义比较函数,功能上与isDeepStrictEqual接近。但两者在一些边界规则上有差异,例如Lodash默认不比较对象的原型,而isDeepStrictEqual会检查原型。如果项目已经在Node.js环境运行,且不需要自定义比较逻辑,直接使用内置方法可以减少依赖、获得更好的性能保障,毕竟核心模块经过了官方充分测试。
四、实际应用场景与注意事项
第一个典型场景是单元测试。无论是自写的轻量测试脚本还是配合测试框架使用,isDeepStrictEqual都能准确验证函数返回的复杂结构是否符合预期,避免逐个字段手动比对的繁琐代码。
第二个场景是配置变更检测。服务运行中定时拉取远端配置,可以先用深度比较判断新旧配置是否一致,只有发生变化时才触发重新加载,减少不必要的初始化开销。第三个场景是缓存失效判断,比如计算数据指纹后与缓存记录对比,决定是否需要重新生成内容。
使用时有几点需要注意。深度比较需要递归遍历所有层级,对于结构极其庞大的对象,性能开销不可忽视,高频调用时应考虑缓存比较结果或改用哈希指纹方案。其次是循环引用问题,虽然算法本身能正确处理循环引用的对象,但如果对象中包含无法枚举或不可比较的宿主对象,结果可能不符合直觉,建议在比较前对数据做清洗。最后,如果你在浏览器环境中开发,由于util是Node.js核心模块,需要借助打包工具或改用其他方案,这一点在选型时要提前确认。
总的来说,util.isDeepStrictEqual是Node.js生态中一个低调但实用的工具方法,规则严谨、零依赖、覆盖了几乎所有内置类型。掌握它的比较规则后,在需要判断结构一致性的场合,你就有了一个可靠的官方标准答案。
Node.jsutil.isDeepStrictEqual对象深度比较修改时间:2026-09-01 02:30:53