为什么JavaScript没有内置方法来检查一个对象是否是纯对象?

3

鉴于自从JavaScript语言诞生以来的发展,为什么没有内置方法来检查一个对象是否是普通对象呢?

或者这个方法实际上存在吗?


4
你在询问什么?"一个对象是'[object Object]'"在实际意义上是什么意思? - Pointy
@Pointy 是的,包含字符串以不匹配字符串。 - guest271314
https://jsfiddle.net/cx0mnuod/ - Ram
3
针对“语言X为什么具有/缺乏特征Y”的问题,最好向语言实现者提问。JavaScript并没有许多东西。 - Pointy
1
问题与之前的问题无关。[] instanceof Object 匹配数组因为它们对象并且原型继承自Object。如果您相对较新于JS,则可能需要一些时间来适应原型继承的概念。然后, o instanceof Object && Array.isArray(o) 应该很清楚(顺便说一句,它不涵盖Object.create(null))。 - Estus Flask
显示剩余21条评论
5个回答

4
您可以通过以下方式检查对象的类型和实例:

var a = new Date();
console.log(typeof a);
console.log(a instanceof Date);

var b = "Hello";
console.log(typeof b);
console.log(b instanceof Date);

根据OP的评论更新:

let arr = [1, 2, true, 4, {
    "abc": 123
  },
  6, 7, {
    "def": 456
  },
  9, [10], {}, "[object Object]"
];
arr.forEach(function(v) {
  if (typeof v == "object" && !(v instanceof Array) && v != null)
    console.log("Object Found");
  else
    ; // console.log("Na");
});

上述代码片段输出三次“Object Found”。

1
我猜这个回答会准确地解答这个问题。 - Estus Flask
1
instanceof 匹配数组,不仅仅是 {} "[object Object]" - guest271314
1
@PraveenKumar 是的。你的答案返回了预期的结果。为什么没有一个内置的 JavaScript 方法来检查一个对象是否是 "[object Object]" - guest271314
1
嗯……我不是开发JavaScript的人,哈哈……实际上,这个问题可以提交给JavaScript工作组。好主意啊伙计。如果你不发,我很快就会发了。 - Praveen Kumar Purushothaman
1
@guest271314 同意。 - Praveen Kumar Purushothaman
显示剩余12条评论

3

没有任何明确的直接方法可以检查一个值是否是对象,即属于Object类型,但有一些可靠的方法可以做到。我在另一个答案中写了一个列表,其中最简洁的方法似乎是

function isObject(value) {
  return Object(value) === value;
}

类似的功能已经在esdiscuss上被多次提出请求。例如,

实际上,Object.isObject 被提出作为稻草人,并且它出现在ES6早期草案中

Object.isObject稻草人最终被拒绝并从ES6草案中删除。

最近,

现在有一个is{Type} Methods阶段0的提案,其中包括Object.isObject和许多其他检查。

因此仍然有希望,最终我们可能会有这样的东西。


上面的内容是关于一般对象测试的。如果你不想这样,你应该定义什么是“普通对象”。
例如,你可以测试constructor属性。但是任何对象都可以自定义它。
你可以使用Object.prototype.toString来获取遗留的ES5 [[Class]]。但是任何对象都可以通过Symbol.toStringTag自定义它。
你可以检查[[GetPrototypeOf]]返回的值。但是即使是奇特的对象,也可能允许将其原型更改为任意对象或null。而代理对象甚至对该内部方法具有完全控制权。
因此,你很可能无法依赖这些测试。并且添加标准可能很困难,因为不同的人可能想要不同的东西。
我想要的是某种方式来检查一个对象是否是普通对象。也就是说,它具有所有对象必须支持的基本内部方法的默认行为。
一旦你知道一个对象是普通的,你就可以依赖像[[GetPrototypeOf]]这样的东西来根据自己的喜好自定义测试。

3
注意,Object 可以作为标识符进行赋值。Object = 1; Object(1) === 1; 会抛出 Uncaught TypeError: Object is not a function 的错误,但 Object === 1 // true 是成立的。 - guest271314
1
@guest271314 在我列出的检查一个值是否为对象的方法中,有一种方法不需要一个合理的环境:new function() { return value; }() === value - Oriol
一直在尝试使用Object.is()Object.getPrototypeOf()Object.isPrototypeOf()。当值为undefined时,会抛出错误。虽然可以链式调用.map(),但需要将值转换回原始值,这就需要额外的调用arr.map(Object).map(Object.getPrototypeOf).filter(Object.is.bind(null, Object.prototype)) - guest271314
1
尝试将函数引用作为回调函数传递,该回调函数调用其自身绑定的函数,并将值作为参数传递给原始函数调用,然后在返回结果之前再次调用参数函数。例如,arr.filter(Object.is.bind(null, Object.prototype, Object.getProtototypeOf /* 将 arr 的当前元素传递给 Object.getProtototypeOf 然后调用 */)),或类似的模式,而不是显式地使用function(element, index, thisArg){};只使用传递当前值的绑定函数引用。或者,确定是否无法实现该模式。 - guest271314
1
这对于数组不起作用,因为它返回true,即使一个数组不是一个纯粹的JavaScript对象。 - KhalilRavanna
显示剩余3条评论

2
依赖于 [object Object] 字符串表示是不准确的。对于任何具有以下特征的对象,此行为可能会更改:
let o = { toString: () => '...' };
('' + o) !== '[object Object]'

var a = [];
a.toString = () => '[object Object]';
('' + a) === '[object Object]';

检查一个值是否为纯对象最可靠的方法是:
let o = {}
Object.getPrototypeOf(o) === Object.prototype

考虑到 constructor 属性没有被篡改,检查一个值是否为普通对象最简单的方法是:
let o = {}
o.constructor === Object

这涵盖了所有从Object构造的POJO,不包括Object.create(null, { ... })或任何子类(包括内置的如RegExpArray):
Object.create(null).constructor !== Object
[].constructor !== Object
(new class {}).constructor !== Object

可能没有专门的方法来检查对象是否是纯对象的原因之一是,限制仅使用 {} 对象是不切实际的。在 JS 的上下文中,这几乎没有意义。这会阻止任何类实例或相对“纯粹”的对象(Object.create({}, ...))的使用。
为了使所需的非纯对象通过检查,需要进行黑客操作:
Object.assign({}, (new class {})).constructor === Object

在大多数情况下,对象检查中,“凡是没有被禁止的都是允许的”原则是行之有效的(对于臭名昭著的“null不一致性”要格外小心)。
将上述原则应用于这种情况,一个安全而简洁的筛选非数组对象的条件是:
o && typeof o === 'object' && !Array.isArray(o)

一个用于过滤非内置对象(函数、ArrayRegExp等)的条件是:
o && (o.constructor === Object || !/\[native code\]/.test(o.constructor))

"POJOs"是什么意思?_"并且不包括"_你能提供一些在答案中没有涵盖的例子吗?这就是问题所在。为什么没有一个专门检查"[object Object]"(即普通对象)的方法呢?你能详细说明为什么检查"[object Object]"不可靠吗?对于第一个例子我不太确定,可以再收集一下。 - guest271314
1
普通的 JavaScript 对象(源自 Java)。这是一个非正式的术语,用于表示 {} 对象(根据上下文可能包括或不包括非 Object Object.create(null, { ... }) 对象)。 - Estus Flask
1
我已经更新了问题,以涵盖之前的问题。原因是这种方法通常是不需要的。对于使用纯对象的限制几乎无法被证明合理,并且“凡事没有禁止就是允许”的原则是值得的。如果开发人员需要特定条件匹配,他/她通常有足够的能力编写一些辅助函数或使用现有的函数(或者两个)。 - Estus Flask
1
目前没有办法。而且未来也不太可能改变,因为直接的父级检查非常低调,它会引起更多问题而不是解决问题 - 这与JS的本质相悖。幸运的是,这只需要一行代码就可以完成。Object.create 本身并没有什么特别之处。特殊情况是 Object.create(null)(对象原型是 undefined),Object.create({})(对象原型是另一个对象)... 实际上,除了 Object.create(Object.prototype) 之外的任何情况都是如此。 - Estus Flask
1
__proto__ 是非标准化的。 === 足够了,Object.is 在这里是多余的(它就是带有额外优势的 ===)。 - Estus Flask
显示剩余14条评论

-1

只是为了进一步记录不同的方法:

我能想到的一种方式是:

JSON.stringify(testValue)[0] === '{';

请记住,具有循环引用的对象无法转换为字符串。然而,如果您确定所有的testValues都不会有循环引用,那么您可以通过检查Null、数组和任何原始值来确保您拥有一个对象。
我建议,如果您计划在整个代码中使用这个功能,最好定义一个辅助函数来实现它,以防您发现它的工作方式与您期望的不同,并最终不得不改变检查方式。

-3

每一个东西在 JavaScript 中都是一个对象,所以没有必要有一个 isObject API


这是不正确的:2true"hello"undefinednullNaN都是非对象的值的例子(尽管typeof null报告它是一个对象)。它们是原始值的示例。 - trincot
从技术上讲,原始类型是对象,即相应构造函数的实例(除了undefinednull)。但它们绝对不是Object对象。 - Estus Flask
@estus,你有关于“原始类型是对象”的参考资料吗?引用自MDN“原始类型(原始值,原始数据类型)是指不是对象的数据”。也许你在想原始包装对象? - trincot
@trincot 不把它们看作对象会更清晰,但实际上它们就是对象,尽管是特殊的对象。MDN上的一句话是错误的,原始值(原始数据类型)是指不是对象且没有方法的数据。原始值不能拥有自己的方法,但可以通过其原型获得方法(并且可以通过原型扩展获得新方法)。''.__proto__ === String.prototype && ''.constructor === String - Estus Flask
1
你在最后展示的是一个强制类型转换的例子:''不是一个对象,但是当你引用一个属性时,JavaScript会将其转换为一个对象。虽然这似乎证明了字符串字面量是一个对象,但实际上并非如此。事实上,''.__proto__被解析为 String('').__proto__。我推荐你阅读《JavaScript原生类型的秘密生活》(The Secret Life of JavaScript Primitives),其中很好地解释了这一点。 - trincot

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