主机对象是否应该在isPlainObject函数中被视为普通对象?
主机对象是否应该在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; } }; }()) });
应该将主机对象视为纯对象吗?
这个问题的出现起因是在不同的浏览器中,使用不同的方法判断一个对象是否是纯对象,结果会有差异。在Opera浏览器中,以下对象通过测试:window.screen
,Math
,JSON
,DOMError
,LSParserFilter
,DOMImplementationLS
,window.opera
,SVGException
和document.implementation
。
这是否是Chrome/Chromium的一个bug?不,不同的函数返回不同的结果并不是一个bug。
对于这四个对象,应该得到什么样的正确结果(这样我就可以确定哪个函数是最准确的)? "正确"如何定义?"纯对象"如何定义?
我相信这三个函数都使用了以下准则:检查一个对象是否是使用"{}"或"new Object"创建的纯对象。然而,这并不是一个有用的标准,因为你遇到差异的对象并不是"创建"的对象——它们是主机对象(甚至是本地对象),只是碰巧存在。
然而,我们可以比较这些函数使用的标准:
- jQuery非常奇怪,你可以在github上查看它的源代码。简而言之:一个[[Class]]不是Boolean Number String Function Array Date RegExp Error
之一,没有真实的nodeName
属性,没有指向自身的window
属性,没有constructor
属性,或者constructor
的prototype
属性有一个自己的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
就足够了(如果你不需要支持跨帧对象)。Math
和JSON
对象将满足这个要求。
- 如果你不希望在原型上有干扰的可枚举属性,那么你可能还可以允许getPrototypeOf(obj) == null
,甚至手动检查可枚举属性,就像lodash做的那样。这将包括Object.prototype
作为一个"纯对象"。
- 如果你希望它可以由new Object
创建,那么还需要添加检查[[Class]]是否为Object
,以排除像JSON
、Math
、Arguments
和所有那些具有特定实现类的主机对象。你真的希望这些对象通过测试isPlainObject
的函数吗?如果它们通过了其他测试,它们会引起混乱吗?
关于iframes的问题是,它们有自己的window
实例,有自己的全局变量,比如Array
。这是一个不同于父窗口的Array
,所以在框架中用字面量创建的数组的instanceof
检查将无法通过父窗口的Array
。
Lo-Dash v1.1.0也会对具有内部[[Class]]不是"Object"的内置对象返回false
。
总之,目前在我编写代码的环境中,这四个函数都可以正常工作,尽管这当然可能会在将来发生改变。我目前的想法是,主机对象不应该被归类为纯对象,这意味着对我来说,utility是正确的,因为它在我测试过的所有浏览器和对象上都给出了这个答案。我不确定如果在跨帧对象的情况下使用会有什么复杂性,我目前的理解还没有达到那个水平。