【问题标题】:Why is there not a built-in method in JavaScript to check if an object is a plain object?为什么 JavaScript 中没有内置方法来检查对象是否为普通对象?
【发布时间】:2017-03-20 06:48:32
【问题描述】:

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

或者该方法实际上是否存在?

【问题讨论】:

  • 如果我理解正确,我认为instance of 是一种方式。
  • 你到底在问什么? an object is "[object Object]" 是什么意思?
  • "[object Object]" 只是 Object.prototype.toString 的输出,其中 any 对象作为参数。
  • “为什么语言 X 有/缺少功能 Y”形式的问题最好针对语言实现者。 JavaScript 没有很多的东西。
  • 该问题与上一个问题无关。 [] instanceof Object 匹配数组,因为它们对象并且原型继承自 Object。如果您对 JS 比较陌生,这可能需要一些时间来习惯原型继承的想法。那么o instanceof Object && Array.isArray(o) 应该很有意义(顺便说一句,它不包括Object.create(null))。

标签: javascript


【解决方案1】:

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

我能想到的一种方式:

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

请记住,具有循环引用的对象不能被字符串化。但是,如果您确定所有 testValues 都不能有循环引用,那么您可以自己检查 Null、Arrays 和任何原始值,以确保您拥有一个 Object。

我建议,如果您打算在整个代码中使用它,那么您定义一个实际实现它的辅助函数,以防您发现它无法按预期工作并最终不得不更改检查方式它。

【讨论】:

    【解决方案2】:

    JavaScript 的每一件事物都是一个 Object ,所以没有必要有一个 isObject api

    【讨论】:

    • 这不是真的:2true"hello"undefinednullNaN 都是非对象值的示例(即使 @987654330 @报告它是一个)。他们是primitive values
    • @trincot 从技术上讲,原语是对象,即各个构造函数的实例(undefinednull 除外)。但它们肯定不是Object 对象。
    • @estus,你有关于“primitives are objects”的参考吗?引自MDN“原始(原始值,原始数据类型)是不是对象的数据”。也许您正在考虑原始包装对象?
    • @trincot 不将它们视为对象并不那么令人困惑,但事实上,它们就是对象,即使是特殊对象。来自 MDN 的声明是不正确的,原始(原始值,原始数据类型)是不是对象且没有方法的数据。基元不能有自己的方法,但可以获取其原型的方法(并且可以通过原型扩展获得新的方法)。 ''.__proto__ === String.prototype && ''.constructor === String.
    • 你最后展示的是一个强制的例子:'' 不是一个对象,但是当你引用这样的属性时,JavaScript 会将它变成一个对象。虽然这 似乎 证明字符串文字是一个对象,但事实并非如此。事实上,''.__proto__ 确实被解析为 String('').__proto__。我可以邀请您阅读The Secret Life of JavaScript Primitives,它很好地解释了这一点。
    【解决方案3】:

    依赖[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
    

    在对象检查的大多数情况下,“所有不被禁止的东西都是允许的”原则是有回报的(特别注意infamous null inconsistency)。

    将上述应用到this case,过滤非数组对象的安全简洁条件是

    o && typeof o === 'object' && !Array.isArray(o)
    

    过滤非内置对象(函数、ArrayRegExp 等)的条件是

    o && (o.constructor === Object || !/\[native code\]/.test(o.constructor))
    

    【讨论】:

    • “POJO”是什么意思? “并且不涵盖” 您能否举例说明 Answer 的方法中涵盖的内容?那是问题。为什么没有专门检查"[object Object]"的方法,即普通对象?您能否详细说明为什么检查 "[object Object]" 不可靠?不确定在这里完全收集第一个示例。
    • 普通的旧 JavaScript 对象(采用 Java)。 {} 对象的非正式术语(可能涵盖也可能不涵盖非对象 Object.create(null, { ... }) 对象,具体取决于上下文。
    • 我已更新问题以涵盖上一个问题。原因是这种方法一般是不需要的。限制使用普通对象很难说得通,“一切不被禁止的都允许”的原则得到了回报。如果开发人员需要特定条件来匹配,他/她通常有相当多的手指来编写一些辅助函数或use an existing one(或two)。
    • 目前没有办法。而且这在未来不太可能改变,因为 直接 亲子检查是非常低调的,它导致的问题比它解决的要多 - 这完全违反了 JS 的性质。幸运的是,这可以通过一行代码完成。 Object.create 本身并没有什么特别之处。特殊情况是Object.create(null)(对象原型是undefined), Object.create({})`(对象原型是另一个对象)......实际上,除了Object.create(Object.prototype)之外的任何东西。
    • __proto__ 是非标准化的。 === 工作,Object.is 在这里过分了(它是 === 有好处)。
    【解决方案4】:

    不存在任何明确的直接方法来检查一个值是否是一个对象,即是否属于 Object 类型,但是有一些万无一失的方法可以做到这一点。 I wrote a list in another answer,好像最简洁了

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

    在 esdiscuss 上已多次请求此类功能。例如,

    事实上,Object.isObjectwas proposed as strawman,还有it appeared in an ES6 early draft

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

    最近,

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

    所以还是有希望的,最终我们可能会有这样的事情。


    以上内容一般用于测试对象。如果您不希望这样,您应该定义“普通对象”对您意味着什么。

    例如,您可以测试constructor 属性。但是任何对象都可以自定义它。

    您可以使用Object.prototype.toString 来获取旧版 ES5 [[Class]]。但任何对象都可以通过Symbol.toStringTag 进行自定义。

    您可以检查 [[GetPrototypeOf]] 返回的值。但即使是外来对象也可能允许将它们的原型更改为任意对象或 null。并且 Proxy 对象甚至可以完全控制该内部方法。

    因此,您很可能无法依赖这些测试。在标准中添加一些东西可能很困难,因为不同的人可能想要不同的东西。

    我想要的是某种方法来检查一个对象是否是一个普通的对象。也就是说,它具有所有对象必须支持的基本内部方法的默认行为。

    一旦您知道一个对象是普通的,您就可以依靠 [[GetPrototypeOf]] 之类的东西来根据您的喜好自定义测试。

    【讨论】:

    • 注意,Object 可以指定为标识符。 Object = 1; Object(1) === 1;Uncaught TypeError: Object is not a function,Object === 1 // true
    • @guest271314 在我检查值是否为对象的方法列表中,有一种方法不需要健全的环境:new function() { return value; }() === value
    • 不确定该模式是如何使用的?一直在尝试确定特定模式是否可能。检查元素是否是普通对象是过程的一部分。另一部分是模式本身。 stackoverflow.com/questions/36232576/… , stackoverflow.com/questions/40541169/…
    • 一直在尝试Object.is()Object.getPrototypeOf() , Object.isPrototypeOf() 当值为 undefined 时抛出错误。可以链接 .map() 调用,但必须转换回原始值,需要额外调用 arr.map(Object).map(Object.getPrototypeOf).filter(Object.is.bind(null, Object.prototype))
    • 试图将函数引用作为回调函数传递,该回调函数使用作为参数传递给原始函数调用的值调用自己的绑定函数,然后在返回结果之前调用具有相同值的参数函数。例如,arr.filter(Object.is.bind(null, Object.prototype, Object.getProtototypeOf /* pass current element of arr` 到 Object.getProtototypeOf 然后调用 */)), or similar pattern, without explicitly using function(element, index, thisArg){}`;仅使用传递当前值的绑定函数引用。或者,确定该模式是否无法实现。
    【解决方案5】:

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

    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 的 cmets 更新

    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");
    });

    上面的代码sn-ps输出三次Object Found

    【讨论】:

    • 我想这会准确地回答这个问题。
    • instanceof 也匹配数组,而不仅仅是{} "[object Object]"
    • @PraveenKumar 是的。您的答案返回预期结果。为什么没有内置的JavaScript 方法来检查对象是否为"[object Object]"
    • @guest271314 呃...:)我没有开发 JavaScript。哈哈...实际上,这可以提交给 JavaScript 工作组。好主意,伙计。如果不是你,我会尽快发布。
    • @guest271314 我不确定。会找你的...:)
    猜你喜欢
    • 2021-10-29
    • 2020-11-27
    • 2010-11-15
    • 2021-10-28
    • 2010-11-30
    • 1970-01-01
    • 2010-09-27
    • 2011-03-27
    • 2021-12-06
    相关资源
    最近更新 更多