如何测试一个空的JavaScript对象?

3893

有时在进行 AJAX 请求后,我的应用程序可能会返回一个空对象,例如:

var a = {};

我该如何检查是否是这种情况?

48个回答

7454

您可以使用 for…in 循环和 Object.hasOwn (ECMA 2022+) 测试来检查对象是否具有任何自有属性:

function isEmpty(obj) {
  for (const prop in obj) {
    if (Object.hasOwn(obj, prop)) {
      return false;
    }
  }

  return true;
}

如果您还需要区分类似{}的空对象和其他没有自己属性的对象(例如Date),则可以进行各种(不幸的是需要特定类型)的类型检查:

function isEmptyObject(value) {
  if (value == null) {
    // null or undefined
    return false;
  }

  if (typeof value !== 'object') {
    // boolean, number, string, function, etc.
    return false;
  }

  const proto = Object.getPrototypeOf(value);

  // consider `Object.create(null)`, commonly used as a safe map
  // before `Map` support, an empty object as well as `{}`
  if (proto !== null && proto !== Object.prototype) {
    return false;
  }

  return isEmpty(value);
}

请注意,像这个例子中与Object.prototype进行比较将无法识别跨域对象。 不要使用Object.keys(obj).length。它的复杂度为O(N),因为它创建一个仅包含所有属性名称的数组,只是为了获取该数组的长度。遍历对象可以实现相同的目标,但其复杂度为O(1)。
为了兼容不支持ES 2022+的JavaScript引擎,可以用var替换const,用Object.prototype.hasOwnProperty.call替换Object.hasOwn
function isEmpty(obj) {
  for (var prop in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, prop)) {
      return false;
    }
  }

  return true
}

许多流行的库也提供了检查空对象的函数:

jQuery

jQuery.isEmptyObject({}); // true

Lodash

_.isEmpty({}); // true

下划线

_.isEmpty({}); // true

Hoek

Hoek.deepEqual({}, {}); // true

ExtJS

Ext.Object.isEmpty({}); // true

AngularJS(版本1)

angular.equals({}, {}); // true

Ramda

R.isEmpty({}); // true

57
Object.keys(new Date()).length === 0 的意思是“新建一个 Date 对象后,获取它的属性名组成的数组,并计算该数组的长度是否为 0”,但这种写法可能会产生歧义。 - cjbarth
2
@Wronski 是的,可能,而且已经在回答中了。不过这不是一个很好的解决方案(速度慢,不够可靠,并且有时候更糟)。 - Ry-
1
@Wronski 这是不够的,因为以下对象也会返回“{}”:{ pirate: undefined }。 - WoodrowShigeru
1
我真的希望这个答案没有被选中。创建一个由未知数量条目组成的新数组,其时间复杂度为 O(N),而且完全没有必要。使用带有早期退出的 for 循环。 - colinta
1
@colinta我明白创建新数组的时间复杂度为O(N),但是循环遍历又有什么好处呢?它的时间复杂度也是O(N),即使有早期退出。你是怎么得出它是O(1)的结论的? - Kyohei Kaneko
显示剩余15条评论

1390
如果支持 ECMAScript 5,你可以使用Object.keys():
function isEmpty(obj) {
    return Object.keys(obj).length === 0;
}

对于ES3及更早版本,没有简单的方法来做到这一点。您需要明确地循环遍历属性:
function isEmpty(obj) {
    for(var prop in obj) {
        if(obj.hasOwnProperty(prop))
            return false;
    }

    return true;
}

73
这段代码运行良好,或更简单地说:function isEmpty(object) { for(var i in object) { return true; } return false; }。 - niczak
55
这个函数中true和false不应该反过来吗? - namtax
47
不 - 这个函数的名字是 isEmpty(),所以如果它有一个属性,它应该返回 false - Christoph
6
另一种写法:function isObjectEmpty(obj) { for (const key in obj) { if (obj.hasOwnProperty(key)) { return false; } } return true; } - Michael Buen
1
@user3840170是正确的!应该更新为Object.hasOwn(obj, prop) - colinta
显示剩余4条评论

591

3
如果您(或任何插件)修改了Object.prototype,则这将无法运行。 - Michał Miszczyszyn
1
请注意,这对于所提出的问题 {} 可以正常工作,但是 jQuery.isEmptyObject([]) === true,可能是因为空列表是可迭代的。 - Paul Sanwald
4
请注意,当您向jQuery.isEmptyObject传递一个空的jQuery对象时(如其API页面中所述),它会返回false。它仅适用于常规JavaScript对象。 - DawnPaladin

390

性能

今天2023年3月20日,我在MacOS Monterey 12.1(M1, 16GB)上使用Chrome v109、Safari v15.2和Firefox v110对选择的解决方案进行测试。

结论

  • 基于for-in(A,L)的解决方案在所有浏览器上都很快或最快
  • 基于JSON.stringify(B)的解决方案在所有浏览器上都最慢

enter image description here

详情

以下代码段中提供了解决方案。如果您想在您的计算机上运行性能测试,请单击

  • 此处获取空对象
  • 此处获取10个字段的对象
  • 此处获取100个字段的对象

此答案的旧版本包含来自2020年的测试 - 此处

答案链接: A, B, C, D, E, F, G, H, I, J, K, L, M, N, O P Q

var log = (s, f) => console.log(`${s} --> {}:${f({})}  {k:2}:${f({ k: 2 })}`);

function A(obj) {
  for (var i in obj) return false;
  return true;
}

function B(obj) {
  return JSON.stringify(obj) === "{}";
}

function C(obj) {
  return Object.keys(obj).length === 0;
}

function D(obj) {
  return Object.entries(obj).length === 0;
}

function E(obj) {
  return Object.getOwnPropertyNames(obj).length === 0;
}

function F(obj) {
  return Object.keys(obj).length === 0 && obj.constructor === Object;
}

function G(obj) {
  return typeof obj === "undefined" || !Object.keys(obj)[0];
}

function H(obj) {
  return Object.entries(obj).length === 0 && obj.constructor === Object;
}

function I(obj) {
  return Object.values(obj).every((val) => typeof val === "undefined");
}

function J(obj) {
  for (const key in obj) {
    if (hasOwnProperty.call(obj, key)) {
      return false;
    }
  }
  return true;
}

function K(obj) {
  var isEmpty = true;
  for (keys in obj) {
     isEmpty = false;
     break;
  }
  return isEmpty;
}

function L(obj) {
  for (var prop in obj) {
    if (obj.hasOwnProperty(prop)) return false;
  }
  return true;
}

function M(obj) {
  if (obj === null || typeof obj !== 'object' ||
    Object.prototype.toString.call(obj) === '[object Array]') {
    return false
  } else {
    for (var prop in obj) {
      if (obj.hasOwnProperty(prop)) {
        return false
      }
    }
    return JSON.stringify(obj) === JSON.stringify({})
  }
}

function N(obj) {
  return (
    Object.getOwnPropertyNames(obj).length === 0 &&
    Object.getOwnPropertySymbols(obj).length === 0 &&
    Object.getPrototypeOf(obj) === Object.prototype
  );
}

function O(obj) {
  return !(Object.getOwnPropertyNames !== undefined
    ? Object.getOwnPropertyNames(obj).length !== 0
    : (function () {
        for (var key in obj) break;
        return key !== null && key !== undefined;
      })());
}

function P(obj) {
  return $.isEmptyObject(obj)
}

function Q(obj) {
  return _.isEmpty(obj);
}

log("A", A);
log("B", B);
log("C", C);
log("D", D);
log("E", E);
log("F", F);
log("G", G);
log("H", H);
log("I", I);
log("J", J);
log("K", K);
log("L", L);
log("M", M);
log("N", N);
log("O", O);
log("P", P);
log("Q", Q);
<script
  src="https://code.jquery.com/jquery-3.6.4.min.js"
  integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8="
  crossorigin="anonymous"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.13.6/underscore-min.js" integrity="sha512-2V49R8ndaagCOnwmj8QnbT1Gz/rie17UouD9Re5WxbzRVUGoftCu5IuqqtAM9+UC3fwfHCSJR1hkzNQh/2wdtg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

enter image description here


很多东西都不合理,因为你把所有的东西都基于false或true的返回值。有时候编程需要if语句或三元运算符。仅供参考。 - Christian Matthew
15
为了完整性,我编辑了你的jsperf以测试 obj = {a:1,b:2,c:3} ,结果显示 for(var i in obj) 仍然是最快的。https://jsperf.com/object-empty-ch/2 - Madacol
1
@Christian Matthew 好的,(o => { for(let k in o) return false; return true; })(obj) - Valen
1
@ChristianMatthew 除了 B-E 外,其余内容都应该放到它们自己的函数中...否则会过于冗长。 - Christian
2
我创建了一个分支测试,将初始对象更改为包含10000个元素,结果非常不同:https://jsbench.me/hplcsahs9e/1。至少在Firefox和Chromium中,“Object.values”明显获胜,这证实了我的其他测试结果:https://jsben.ch/a3bT7。无论如何,谢谢!我只是认为答案需要编辑。 - kcpr
2
Moronix的基准测试比这个更好。大多数代码片段都不相等,因此将它们进行比较没有太多意义。(K)和(L)基本上是相同的代码,尽管前者缺少了一个return true,但其中一个比另一个慢25%……这表明差异是如此微小,以至于它们不应该有任何影响,它们将被包装在必然的函数调用中,在JIT和垃圾收集抖动以及其他噪声中将被淹没。 - user3840170

236

32
或者您可以使用lodash的isEmpty函数(http://lodash.com/docs#isEmpty),但这与使用jQuery解决方案有何不同- 您仍需要安装额外的库。我认为原意是要使用纯JavaScript解决方案。 - tim-montague
6
如果将整个依赖项添加到您的超级高性能依赖应用程序中会令人尴尬,您可以只安装 _.isEmptynpm i lodash.isempty - Nick Bull

135

7
这包括不可枚举的属性,如果您在意的话。 - user663031
Object.getOwnPropertyNames({}).length比(for...in...)选项慢10倍,我建议避免使用它来测试对象是否为空。 - davidhadas
5
“Object.getOwnPropertyNames(new Date()).length === 0”这个答案可能会误导。意思是说这个答案可能不准确。 - cjbarth

105

使用JSON.stringify怎么样?现代浏览器几乎都支持它。

function isEmptyObject(obj){
    return JSON.stringify(obj) === '{}';
}

39
这种工具需要快速运行,速度很重要。这里有一个快速性能测试链接:http://jsperf.com/empty-object-test - user81962
2
这是一个非常慢的选项 - 我建议使用(for...in)选项代替。 - davidhadas
4
它不能用于包含函数的对象。 - Felix Kling
6
如果对象中存在循环引用,它也会抛出错误。因此,它速度慢、不可靠,可能会抛出错误并破坏其他所有内容。没有任何理由使用它。 - Burak
2
请注意 JSON.stringify(new Error('gotcha')) === '{}'true - onestep.ua
显示剩余2条评论

93

如果你使用较新的浏览器,有一种简单的方法。 Object.keys(obj).length === 0


1
“keys”属性从哪里来? - user663031
4
这是 ECMAScript 5.1 中的一个标准方法 - Erin
1
以上评论怎么会有4个赞?是的,Object.keys是一个标准方法,但对象没有一个名为keys的属性。因此,这段代码将报告任何对象为空,除非它偶然地具有一个名为key的属性,并且该属性的值再次具有一个名为length的属性,其值不为零。太可怕了! - scravy
2
Object.keys(new Date()).length === 0;因此这个答案可能会误导。 - cjbarth
25
@scravy Object 是 Object 类的一个实例。Object 有一个名为 'keys' 的静态方法,它接受一个对象作为参数。该方法返回一个字符串数组,其中字符串是属性名称。 参考链接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/keys - Sgnl

64

虽然这是一个旧问题,但我最近也遇到了同样的问题。如果你只是想检查对象是否为空,那么包含JQuery并不是一个好主意。相反,深入了解JQuery的代码,你就会得到答案。

function isEmptyObject(obj) {
    var name;
    for (name in obj) {
        if (obj.hasOwnProperty(name)) {
            return false;
        }
    }
    return true;
}

5
只有在其他进程没有向您的基础对象添加原型时,这才有用。要使其真正可行,您需要测试obj.hasOwnProperty(name)。 - mpemburn
isEmptyObject({ hasOwnProperty: "where is your god now?" }) - user3840170

50

使用Object.keys(obj).length(如上所建议的ECMA 5+)对于空对象来说要慢10倍!继续使用老式方式(for...in)。

在Node、Chrome、Firefox和IE 9下进行测试,很明显在大多数情况下:

  • (for...in...)是使用最快的选项!
  • 对于空对象,Object.keys(obj).length要慢10倍。
  • JSON.stringify(obj).length始终是最慢的(不出所料)。
  • Object.getOwnPropertyNames(obj).length比Object.keys(obj).length慢得多 在某些系统上可能会慢很多。

总之,在性能方面,应该使用:

function isEmpty(obj) { 
   for (var x in obj) { return false; }
   return true;
}
或者
function isEmpty(obj) {
   for (var x in obj) { if (obj.hasOwnProperty(x))  return false; }
   return true;
}

请查看对象是否为空?的详细测试结果和测试代码。


1
Object.keys 操作较慢,但代码量较少。在一个小页面上,如果调用次数不超过 10 次,那么考虑到额外的解析时间,这是否仍会更慢? - yankee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接