如何区分对象字面量和其他 Javascript 对象?
更新:我正在重新表述这个问题,因为对我来说重要的一点是识别对象文字:
如何区分对象文字和任何其他 Javascript 对象(例如 DOM 节点、日期对象等)?我怎样才能编写这个函数:
function f(x) {
if (typeof x === 'object literal')
console.log('Object literal!');
else
console.log('Something else!');
}
这样它只打印对象文字!
作为下面第一次调用的结果:
f({name: 'Tom'});
f(function() {});
f(new String('howdy'));
f('hello');
f(document);
原始问题
我正在编写一个设计的Javascript函数接受对象文字、字符串或 DOM 节点作为其参数。它需要以稍微不同的方式处理每个参数,但目前我无法弄清楚如何区分 DOM 节点和普通的旧对象文字。
这是我的函数的一个大大简化的版本,以及对我需要处理的每种参数的测试:
function f(x) {
if (typeof x == 'string')
console.log('Got a string!');
else if (typeof x == 'object')
console.log('Got an object literal!');
else
console.log('Got a DOM node!');
}
f('hello');
f({name: 'Tom'});
f(document);
此代码将为后两个调用记录相同的消息。我不知道要在 else if
子句中包含什么内容。我尝试过其他具有相同效果的变体,例如 x instanceof Object
。
我知道这对我来说可能是糟糕的 API/代码设计。即使是这样,我仍然想知道如何做到这一点。
Update: I'm rephrasing this question, because the important point to me is identifying the object literal:
How can I tell the difference between an object literal and any other Javascript object (e.g. a DOM node, a Date object, etc.)? How can I write this function:
function f(x) {
if (typeof x === 'object literal')
console.log('Object literal!');
else
console.log('Something else!');
}
So that it only prints Object literal!
as a result of the first call below:
f({name: 'Tom'});
f(function() {});
f(new String('howdy'));
f('hello');
f(document);
Original Question
I'm writing a Javascript function that is designed to accept an object literal, a string, or a DOM node as its argument. It needs to handle each argument slightly differently, but at the moment I can't figure out how to differentiate between a DOM node and a plain old object literal.
Here is a greatly simplified version of my function, along with a test for each kind of argument I need to handle:
function f(x) {
if (typeof x == 'string')
console.log('Got a string!');
else if (typeof x == 'object')
console.log('Got an object literal!');
else
console.log('Got a DOM node!');
}
f('hello');
f({name: 'Tom'});
f(document);
This code will log the same message for the second two calls. I can't figure out what to include in the else if
clause. I've tried other variations like x instanceof Object
that have the same effect.
I understand that this might be bad API/code design on my part. Even if it is, I'd still like to know how to do this.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
简短的回答是你不能。
对象文字类似于:
而使用对象构造函数创建的同一对象可能是:
我认为没有任何可靠的方法来区分创建了两个对象。
为什么它很重要?
一般的功能测试策略是测试传递给函数的对象的属性,以确定它们是否支持要调用的方法。这样你就不必真正关心对象是如何创建的。
您可以使用“鸭子打字”,但仅限于有限的范围。例如,您不能仅仅因为对象具有
getFullYear()
方法就保证它是 Date 对象。同样,仅仅因为它具有 nodeType 属性并不意味着它是一个 DOM 对象。例如,jQuery isPlainObject 函数认为,如果一个对象具有 nodeType 属性,则它是 DOM 节点,如果它具有 setInterval 属性,则它是 Window 对象。这种鸭子类型非常简单,并且在某些情况下会失败。
您可能还注意到,jQuery 依赖于以特定顺序返回的属性 - 另一个危险的假设,任何标准都不支持(尽管一些支持者正在尝试更改标准以适应他们假设的行为)。
2014 年 4 月 22 日编辑:在版本 1.10 中,jQuery 包含一个 support.ownLast 属性,该属性基于测试单个属性(显然这是为了 IE9 支持),以查看继承的属性是首先枚举还是最后枚举。这仍然忽略了一个事实,即对象的属性可以以任何顺序返回,无论它们是继承的还是自己的,并且可能会混乱。
对于“普通”对象来说,最简单的测试可能是:
对于使用对象文字或对象构造函数创建的对象来说,这始终是正确的,但对于以其他方式创建的对象来说,很可能给出虚假结果,并且可能(可能会)跨帧失败。您也可以添加一个
instanceof
测试,但我看不出它做了任何构造函数测试没有做的事情。如果您传递 ActiveX 对象,最好将其包装在 try..catch 中,因为它们可能返回各种奇怪的结果,甚至引发错误。
编辑 2015 年 10 月 13 日
当然,这里存在一些陷阱:
弄乱构造函数属性会导致问题。还有其他陷阱,例如由 Object 之外的构造函数创建的对象。
由于 ES5 现在几乎无处不在,因此有 Object.getPrototypeOf 检查对象的
[[Prototype]]
。如果它是 Object.prototype 中的构建,那么该对象就是一个普通对象。然而,一些开发人员希望创建没有继承属性的真正“空”对象。这可以通过以下方式完成:在本例中,
[[Prototype]]
属性为 null。因此,仅仅检查内部原型是否是 Object.prototype 是不够的。还有一种相当广泛使用的:
被指定为基于内部
[[Class]]
属性返回一个字符串,对于对象来说是 [object Object]。然而,这种情况在 ECMAScript 2015 中发生了变化,因此可以对其他类型的对象执行测试,默认值为 [object Object],因此该对象可能不是“普通对象”,而只是一个不被识别为其他对象的对象。因此,该规范指出:http://www.ecma-international.org/ecma-262/6.0/index.html#sec-object.prototype.tostring
因此,更新的函数允许 ES5 之前的主机,
[[Prototype]]
为 null 的对象以及其他没有 getPrototypeOf 的对象类型(例如 null,谢谢 Chris Nielsen)请注意,无法填充 getPrototypeOf,因此可能不会。如果需要支持较旧的浏览器(例如 IE 8 及更低版本,根据 MDN)。
The short answer is you can't.
An object literal is something like:
whereas the same object created using the Object constructor might be:
I don't think there is any reliable way to tell the difference between how the two objects were created.
Why is it important?
A general feature testing strategy is to test the properties of the objects passed to a function to determine if they support the methods that are to be called. That way you don't really care how an object is created.
You can employ "duck typing", but only to a limited extent. You can't guarantee that just because an object has, for example, a
getFullYear()
method that it is a Date object. Similarly, just because it has a nodeType property doesn't mean it's a DOM object.For example, the jQuery
isPlainObject
function thinks that if an object has a nodeType property, it's a DOM node, and if it has asetInterval
property it's a Window object. That sort of duck typing is extremely simplistic and will fail in some cases.You may also note that jQuery depends on properties being returned in a specific order - another dangerous assumption that is not supported by any standard (though some supporters are trying to change the standard to suit their assumed behaviour).
Edit 22-Apr-2014: in version 1.10 jQuery includes a support.ownLast property based on testing a single property (apparently this is for IE9 support) to see if inherited properties are enumerated first or last. This continues to ignore the fact that an object's properties can be returned in any order, regardless of whether they are inherited or own, and may be jumbled.
Probably the simplest test for "plain" objects is:
Which will always be true for objects created using object literals or the Object constructor, but may well give spurious results for objects created other ways and may (probably will) fail across frames. You could add an
instanceof
test too, but I can't see that it does anything that the constructor test doesn't.If you are passing ActiveX objects, best to wrap it in try..catch as they can return all sorts of weird results, even throw errors.
Edit 13-Oct-2015
Of course there are some traps:
Messing with the constructor property will cause issues. There are other traps too, such as objects created by constructors other than Object.
Since ES5 is now pretty much ubiquitous, there is Object.getPrototypeOf to check the
[[Prototype]]
of an object. If it's the buit–in Object.prototype, then the object is a plain object. However, some developers wish to create truly "empty" objects that have no inherited properties. This can be done using:In this case, the
[[Prototype]]
property is null. So simply checking if the internal prototype is Object.prototype isn't sufficient.There is also the reasonably widely used:
that was specified as returning a string based on the internal
[[Class]]
property, which for Objects is [object Object]. However, that has changed in ECMAScript 2015 so that tests are performed for other types of object and the default is [object Object], so the object may not be a "plain object", just one that isn't recognised as something else. The specification therefore notes that:http://www.ecma-international.org/ecma-262/6.0/index.html#sec-object.prototype.tostring
So an updated function that allows for pre–ES5 hosts, objects with a
[[Prototype]]
of null and other object types that don't have getPrototypeOf (such as null, thanks Chris Nielsen) is below.Note that there is no way to polyfill getPrototypeOf, so may not be useful if support for older browsers is required (e.g. IE 8 and lower, according to MDN).
类似于@RobG示例:
测试:
Similar to @RobG example:
TEST:
由于所有 DOM 节点都继承自 Node 接口,您可以尝试以下操作:
但我不确定这是否适用于旧版本的 Internet Explorer
Since all DOM Nodes inherit from the Node interface you could try the following:
But I'm not sure if this works in older versions of Internet Explorer
将 DOM 节点的检查移到对象文字上方。检查 DOM 节点上存在的某些属性来检测节点。我正在使用
nodeType
。它不是很万无一失,因为您可以传入一个对象{nodeType: 0 }
,这会破坏这个。像上面这样的所有鸭子类型检查,甚至
instanceof
检查都注定会失败。要真正确定给定对象是否实际上是 DOM 节点,您需要使用传入对象本身以外的其他内容。Move the check for DOM node above the object literal. Check some property that exists on a DOM node to detect a node. I am using the
nodeType
. It's not very foolproof as you could pass in an object{nodeType: 0 }
and that would break this.All duck typing checks like the one above and even
instanceof
checks are bound to fail. To truly determine if the given object is actually a DOM node, you need to use something other than the passed-in object itself.也许是这样的?
或者另一种方法:
Maybe something like this?
Or this other approach:
如果您不介意使用软件包,我建议使用 lodash 来实现此目的:
https://lodash .com/docs/4.17.15#isPlainObject
If you don't mind using a package i would recommend using lodash for this:
https://lodash.com/docs/4.17.15#isPlainObject