0

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

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

  • jQuery
  • 罗达什
  • 实用程序(我正在研究的一个库)
  • 替代方案,在下面的评论中建议

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

在 Chrome 上运行时对 jsfiddle 的测试

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 构造函数创建的对象。

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

目前,实用程序是一致的并且说它们不是,其他例程为不同浏览器上的主机对象给出不同的结果。

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

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;
            }
        };
    }())
});
4

1 回答 1

1

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

不,至少在 Opera 中,小提琴中的测试失败了window.screen, Math, JSON, DOMError, LSParserFilter, DOMImplementationLS, window.opera, SVGException, 和document.implementation.

这是 Chrome/Chromium 中的错误吗?

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

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

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

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

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

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

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

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

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

  • 实用程序有点混淆,但检查本身很简单:一个对象,其 [[Class]] 是Object并且其原型是Object.prototype(或者更确切地说,其原型具有isPrototypeOf产生truefor的方法{})。

  • Lodash有一些奇怪的东西,比如 jQuery,但并不难——你可以在 github 上阅读源代码。它首先检查对象类型而不是 null,然后从方法中获取Object.prototypevia (如果存在)。如果它找到一个,则该对象或其原​​型必须是该对象,并且它不能是一个对象。如果它没有找到一个,它会退回到我不会在这里解释的垫片。getPrototypeOf(getPrototypeOf(…))valueOfObject.prototypeArguments

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

  • 替代”实现:这将测试对象的原型要么null(但明确排除Object.prototype)要么是Object.prototype,并且 [[Class]] 值是Object

主机对象应该算作普通对象吗?

也许。这始终取决于您的用例……</p>

  • 表现得好像它是由new Object

    那么getPrototypeOf(obj) == Object.prototype应该没问题(如果您不需要支持跨框架对象)。Math和对象将JSON满足此要求。

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

    然后,您可能还允许getPrototypeOf(obj) == null甚至手动检查 lodash 那样的可枚举属性。例如,这现在将包括Object.prototype为“普通对象”。

  • 可创建者new Object

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

另请参阅说什么?在 niftysnippets.org(TJCrowder 的博客)

于 2013-03-10T13:52:57.467 回答