JavaScript中判断一个对象是否为普通对象的方法

10

最近我发现了一个 JavaScript 函数,用于判断某个东西是否为纯对象:

function isPlainObject (value){
  if (typeof value !== 'object' || value === null) return false;

  let proto = value;
  while (Object.getPrototypeOf(proto) !== null) {
    proto = Object.getPrototypeOf(proto);
  }

  return Object.getPrototypeOf(value) === proto;
};

来源:https://github.com/redux-utilities/redux-actions/blob/master/src/utils/isPlainObject.js

我想知道:

  1. 下面的方法是否完全相同?
  2. 如果是,它是否可以被认为更有效?
function isPlainObj(value){
  if (typeof value !== 'object' || value === null) return false;
  let obj = {};
  return Object.getPrototypeOf(value) === Object.getPrototypeOf(obj)
}

2
value是一个普通对象时,Object.prototype.toString.call(value)返回[object Object] - Teemu
这两个句柄对象都无法正确处理通过Object.create(null)创建的空原型对象。 - James
4个回答

25

检查一个值是否为普通对象:

/*
 isPlainObject({}) // true
 isPlainObject() // false
 isPlainObject(null) // false
 isPlainObject(true) // false
 isPlainObject('1') // false
 isPlainObject([]) // false
 isPlainObject(new Function()) // false
 isPlainObject(new Date()) // false
 */
const isPlainObject = value => value?.constructor === Object;

不包括null、标量、数组、函数以及任何扩展类型,除了Object。


这对使用空原型创建的对象无效(Object.create(null).constructorundefined)。 - James
是的,@James,很好的观点。没有构造函数的对象将返回false。 - Javier Rey
1
为了包含没有构造函数的对象: const isPlainObject = value => value && [undefined, Object].includes(value.constructor); - Javier Rey

2
  1. 不行。前者遍历整个原型链,但仅在该链由1个原型组成时返回true(因此,您的第一个示例有点无意义)。
  2. 是和否。是的,它更有效率,不需要循环所有内容来检查原型是否为Object.prototype。否则,它会执行不必要的操作。

这是我会这样做:

const isPlainObj = value => !!value &&
                            Object.getPrototypeOf(value) === Object.prototype;

如果您只想知道value原型是Object.prototype,那么无需过于花哨。

P.S. 您的初始示例与其他示例(包括我的)不同之处在于它可以处理外部对象,即来自不同领域(例如iframes)的对象。我不认为这种用例存在于2021年,但是如果您的应用程序/网站在不同的窗口/框架之间传递对象,那么第一个函数也将为这些对象返回true,而我的建议或者您的建议则不会。然而,没有必要遍历整个链,您可以简单地执行以下操作:

function isPlainObj(value) {
          // it's truthy
  return  !!value &&
          // it has a prototype that's also truthy
          !!(value = Object.getPrototypeOf(value)) &&
          // which has `null` as parent prototype
          !Object.getPrototypeOf(value);
}

这个函数获取proto一次或最多两次,确保其链以null结尾,通常是常见的字面量情况。
然而,我认为外部对象现在不存在,所以我会坚持使用我建议的版本。

2

无论是 OP 问题中提供的方法还是 @Javier Rey 的答案,在某些情况下都不能很好地工作。Atomics,具有 Symbol.iteratorSymbol.toStringTag 静态数据属性的对象,以及 JSON 命名空间对象

以下测试用例将失败:

const iterable1 = {};

iterable1[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};

isPlainObject(iterable1) // true, should be false
isPlainObject(JSON) // true, should be false

因此,我们需要对这些对象进行额外的检查。

export default function isPlainObject(value) {
    if (typeof value !== 'object' || value === null) {
        return false;
    }

    const prototype = Object.getPrototypeOf(value);
    return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in value) && !(Symbol.iterator in value);
} 

-1

ToolJS 在其“Obj”模块下拥有一种方法,可以检查对象是否实际上是一个普通的对象文字。

参考: ToolJS对象模块

获取代码,请访问NPM或通过其CDN,并按以下方式使用

// export the methods in the "Obj" module
var $ = ToolJS.export("Obj");

var myObj = {name: "John Doe"};
var myArr = [1,2,3]; // note that arrays are of type object but are obviously not plain objects
var myEl = document.getElementById("elem"); // elements are also objects

$.isObj(myObj); // => true
$.isObj(myArr); // => false
$.isObj(myEl); // => false

您可以在此处查看完整文档链接

在底层,该方法检查项目类型不为null或undefined但为对象,然后检查其构造函数以查看其是否为对象,之后确保其不是数组,最后将其转换为字符串以查看其是否为纯对象。


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