【问题标题】:Should host objects be counted as plain objects in an isPlainObject function?在 isPlainObject 函数中,宿主对象是否应该算作普通对象?
【发布时间】:2013-02-25 06:40:47
【问题描述】:

我一直在对不同浏览器上不同库中的 isPlainObject 函数进行一些测试。

有 4 种不同的(代码方面的)isPlainObject 函数正在广泛的对象上进行测试:

  • jquery
  • lodash
  • 实用程序(我正在开发的一个库)
  • 替代方案,在下面的 cmets 中建议

以上所有四个都显示了 Chrome v23.0.1271.95 到 Chrome v25.0.1364.160、FireFox v 19.0 和 Opera v12.14 的差异,但实用程序至少对所有这些对象给出了相同的 false 响应浏览器

The tests on jsfiddle 在 Chrome 上运行时

Failed to agree: JSON - jquery: true - utility: false - lodash: true - alt: false
Failed to agree: Math - jquery: true - utility: false - lodash: true - alt: false
Failed to agree: top - jquery: false - utility: false - lodash: true - alt: true
Failed to agree: parent - jquery: false - utility: false - lodash: true - alt: true
  • true 是例程认为对象是普通的,false 是不普通的

编辑:我相信所有例程都使用以下类似的标准

jquery states
检查对象是否为普通对象(使用“{}”或“new Object”创建)。

lodash states
检查给定值是否是由 Object 构造函数创建的对象。

我知道宿主对象与使用“{}”或“new Object”构造的对象不同,所以我想我的问题是:宿主对象应该算作普通对象吗?

目前,实用程序是一致的,但表示它们不是,其他例程在不同浏览器上对主机对象给出不同的结果。

编辑:结果的准确性对我来说是最重要的因素,性能是次要考虑因素。

jsperf 上提供了 3 个库的性能结果和建议的替代方案

编辑:这是实用程序库函数,因此您无需搜索代码。

defineProperty(utility, "isPlainObject", {
    value: (function () {
        var o = {};

        return function (obj) {
            try {
                return utility.isObject(obj) && getPrototypeOf(obj).isPrototypeOf(o);
            } catch (e) {
                return false;
            }
        };
    }())
});

【问题讨论】:

  • jquery 库文档前面说,这可能不适用于您正在测试的类型的对象:注意:主机对象(或浏览器主机环境用于完成 ECMAScript 执行环境的对象)具有许多难以稳健地检测跨平台特征的不一致。因此,在某些情况下,$.isPlainObject() 可能会在不同的浏览器中进行不一致的评估。
  • 那么三个不同的代码库给出了 3 个不同的结果?也许是因为它们是不同的库,并且有不同的标准。
  • ...这么说来,要怎么定义一个普通的对象呢?
  • 检查对象是否为普通对象(使用“{}”或“new Object”创建)。
  • 您列出的标准并不是真正的标准。只有检查对象的某些方面才能尝试推断它是否是“普通对象”。目前没有内置方法。因此,每个人都有自己的判断标准,而且没有绝对完美的方法。

标签: javascript jquery javascript-objects lodash


【解决方案1】:

在 FireFox v 19.0 和 Opera v12.14 上执行时,上述三个都通过了测试

不,至少在 Opera 中,window.screenMathJSONDOMErrorLSParserFilterDOMImplementationLSwindow.operaSVGException 和 @ 的测试失败987654333@.

这是 Chrome/Chromium 中的错误吗?

什么?不同的函数返回不同的结果?没有。

对于四个对象中的每一个,正确的结果应该是什么(以便我可以确定哪个函数最准确)?

您如何定义“正确”?你如何定义“普通对象”?

我相信所有 3 个例程都使用以下标准:

检查对象是否为普通对象(使用“{}”或“new Object”创建)。

这几乎不是一个有用的标准,因为您遇到差异的那些对象不是“创建”的——它们是恰好存在的宿主对象(甚至是本地对象)。

然而,我们可以比较这些函数使用的标准:

  • jQuery 很奇怪;你可以阅读它的source code at github。简而言之:一个对象或函数,其 [[Class]] 不是 Boolean Number String Function Array Date RegExp Error 之一,没有真实的 nodeName 属性,没有指向自身的 window 属性,并且没有 @987654337 @ 属性或constructorprototype 属性有自己的isPrototypeOf 属性。

    他们这样做似乎是为了支持跨浏览器,但正如您所见,在某些情况下它会失败,因为您不希望它是一个普通对象。

  • Utility 有点混淆,但检查本身很简单:一个对象,其 [[Class]] 为 Object,其原型为 Object.prototype(或者更确切地说,其原型具有isPrototypeOf 方法为 {} 生成 true)。

  • Lodash 有一些奇怪的东西,比如 jQuery,但并不难——你可以阅读 source at github。它首先检查对象类型而不是 null,然后如果存在,则通过 getPrototypeOf(getPrototypeOf(…))valueOf 方法获取 Object.prototype。如果找到了,则该对象或其原​​型必须是 Object.prototype 对象,并且不能是 Arguments 对象。如果它没有找到,它会退回到shim,我不会在这里解释。

    所有这些都是为了支持在不提供getPrototypeOf 方法的浏览器中使用不同的Object.prototype 对象检测来自不同环境(例如iframe)的普通对象。

  • "Alternative" 实现:这会测试对象的原型是null(但明确排除Object.prototype)还是Object.prototype 和[[Class]] 值成为Object

应该把宿主对象算作普通对象吗?

也许吧。这始终取决于您的用例……

  • 就像它是由new Object创建的一样

    那么只需getPrototypeOf(obj) == Object.prototype 就可以了(如果您不需要支持跨框架对象)。 MathJSON 对象将满足此要求。

  • 在原型上没有干扰的可枚举属性

    那么你也可以允许getPrototypeOf(obj) == null 或者甚至像lodash 那样手动检查可枚举的属性。例如,这将包括 Object.prototype 作为“普通对象”。

  • 可由new Object创建

    然后还添加检查 [[Class]] 为 Object 以排除本机对象,如 JSONMathArguments 以及所有具有特定于实现类的主机对象。你真的希望那些在测试isPlainObject 的函数中通过,如果它们通过其他测试会造成严重破坏吗?

另见Say what? at niftysnippets.org(T.J.Crowder 的博客)

【讨论】:

  • 我真的应该编辑其中的一些 cmets,因为我原来的小提琴有一些问题已经得到纠正。所以是的,FireFox 和 Opera 有更多的差异,尽管实用程序函数似乎在所有 3 个浏览器上都一致,并且对于所有差异都返回 false。我不知道如何更好地表达标准,我理解你和他们关于宿主对象而不是构造的文档,但我认为本质还是应该理解?
  • 感谢您抽出宝贵时间就该问题提出思考和意见。目前,所有 4 个函数在我正在为其编写代码的环境中都可以正常工作,尽管这在未来当然可能会改变。我目前的想法是不应将宿主对象归类为普通对象,这意味着该实用程序对我来说是正确的,因为它在我迄今为止测试过的所有浏览器和对象中都给出了这个答案。我不确定如果在跨框架对象场景中使用这会带来什么复杂性,我目前的理解还没有达到那个水平。
  • iframe 的问题在于它们有自己的 window 实例和自己的全局变量,例如 Array。这与父窗口的Array 不同的Array,因此instanceof 检查使用框架中脚本中的文字创建的数组将对父窗口Array 失败。
  • Lo-Dash v1.1.0 将返回 false 用于具有除“对象”之外的内部 [[Class]] 的内置函数。
猜你喜欢
  • 1970-01-01
  • 2021-10-29
  • 1970-01-01
  • 2017-04-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-12
  • 2017-09-10
  • 2010-10-15
相关资源
最近更新 更多