主机对象是否应该在isPlainObject函数中被视为普通对象?

10 浏览
0 Comments

主机对象是否应该在isPlainObject函数中被视为普通对象?

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

有4个不同的(在代码上)isPlainObject函数正在对各种对象进行测试:

  • jquery
  • lodash
  • utility(我正在开发的一个库)
  • 评论中提到的替代方法

以上这四个库在Chrome v23.0.1271.95到Chrome v25.0.1364.160、FireFox v19.0和Opera v12.14上都表现出差异,但至少在所有浏览器上,utility对这些对象的响应都是false。

在jsfiddle上运行的测试 在Chrome上

无法达成一致意见:JSON - jquery: true - utility: false - lodash: true - alt: false
无法达成一致意见:Math - jquery: true - utility: false - lodash: true - alt: false
无法达成一致意见:top - jquery: false - utility: false - lodash: true - alt: true
无法达成一致意见:parent - jquery: false - utility: false - lodash: true - alt: true

  • true表示该程序认为该对象是普通对象,false表示不是普通对象

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

jquery说明

检查一个对象是否是普通对象(使用"{}"或"new Object"创建)。

lodash说明

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

我知道宿主对象与使用"{}"或"new Object"构造的对象不同,所以我想问:宿主对象应该被视为普通对象吗?

目前,utility保持一致,并表示它们不是普通对象,其他程序在不同浏览器上对宿主对象给出了不同的结果。

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

三个库和建议的替代方法的性能结果可以在jsperf上找到。

编辑:这是utility库中的函数,所以你不需要搜索代码。

defineProperty(utility, "isPlainObject", {
    value: (function () {
        var o = {};
        return function (obj) {
            try {
                return utility.isObject(obj) && getPrototypeOf(obj).isPrototypeOf(o);
            } catch (e) {
                return false;
            }
        };
    }())
});

0
0 Comments

应该将主机对象视为纯对象吗?

这个问题的出现起因是在不同的浏览器中,使用不同的方法判断一个对象是否是纯对象,结果会有差异。在Opera浏览器中,以下对象通过测试:window.screenMathJSONDOMErrorLSParserFilterDOMImplementationLSwindow.operaSVGExceptiondocument.implementation

这是否是Chrome/Chromium的一个bug?不,不同的函数返回不同的结果并不是一个bug。

对于这四个对象,应该得到什么样的正确结果(这样我就可以确定哪个函数是最准确的)? "正确"如何定义?"纯对象"如何定义?

我相信这三个函数都使用了以下准则:检查一个对象是否是使用"{}"或"new Object"创建的纯对象。然而,这并不是一个有用的标准,因为你遇到差异的对象并不是"创建"的对象——它们是主机对象(甚至是本地对象),只是碰巧存在。

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

- jQuery非常奇怪,你可以在github上查看它的源代码。简而言之:一个[[Class]]不是Boolean Number String Function Array Date RegExp Error之一,没有真实的nodeName属性,没有指向自身的window属性,没有constructor属性,或者constructorprototype属性有一个自己的isPrototypeOf属性。

它们似乎这样做是为了跨浏览器兼容性,但是你可以看到它在一些情况下失败了,而你本来期望它不是一个纯对象。

- Utility有点混淆,但检查本身很简单:一个[[Class]]是Object,它的原型是Object.prototype(或者说,原型有一个isPrototypeOf方法,对{}返回true)。

- Lodash和jQuery一样有一些奇怪的地方,但不那么复杂。它首先检查对象类型而不是null,然后通过valueOf方法从一个Object.prototype获取Object.prototype,如果存在的话。如果找到一个,该对象或其原型必须是那个Object.prototype对象,并且不能是一个Arguments对象。如果没有找到,则回退到我这里不打算解释的shim

所有这些事情都是为了支持检测来自不同环境(例如iframes)的纯对象,这些环境具有不同的Object.prototype对象,并且在不提供getPrototypeOf方法的浏览器中。

- "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的函数吗?如果它们通过了其他测试,它们会引起混乱吗?

关于iframes的问题是,它们有自己的window实例,有自己的全局变量,比如Array。这是一个不同于父窗口的Array,所以在框架中用字面量创建的数组的instanceof检查将无法通过父窗口的Array

Lo-Dash v1.1.0也会对具有内部[[Class]]不是"Object"的内置对象返回false

总之,目前在我编写代码的环境中,这四个函数都可以正常工作,尽管这当然可能会在将来发生改变。我目前的想法是,主机对象不应该被归类为纯对象,这意味着对我来说,utility是正确的,因为它在我测试过的所有浏览器和对象上都给出了这个答案。我不确定如果在跨帧对象的情况下使用会有什么复杂性,我目前的理解还没有达到那个水平。

0