如何区分对象文字和其他 Javascript 对象?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5876332/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-25 18:39:00  来源:igfitidea点击:

How can I differentiate between an object literal other Javascript objects?

javascripttypescomparisonobject-literal

提问by Will McCutchen

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:

如何区分对象字面量和任何其他 Javascript 对象(例如 DOM 节点、Date 对象等)?我该如何编写这个函数:

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:

这样它只会Object literal!作为下面第一次调用的结果打印:

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.

我正在编写一个 Javascript 函数,该函数旨在接受对象文字、字符串或 DOM 节点作为其参数。它需要稍微不同地处理每个参数,但目前我无法弄清楚如何区分 DOM 节点和普通的旧对象文字。

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 ifclause. I've tried other variations like x instanceof Objectthat have the same effect.

此代码将为后两个调用记录相同的消息。我不知道要在else if条款中包含什么。我试过其他类似的变体x instanceof Object也有同样的效果。

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.

我知道这对我来说可能是糟糕的 API/代码设计。即使是这样,我仍然想知道如何做到这一点。

回答by RobG

How can I tell the difference between an object literal and any other Javascript object (e.g. a DOM node, a Date object, etc.)?

如何区分对象字面量和任何其他 Javascript 对象(例如 DOM 节点、Date 对象等)?

The short answer is you can't.

简短的回答是你不能。

An object literalis something like:

一个对象文字是这样的:

var objLiteral = {foo: 'foo', bar: 'bar'};

whereas the same object created using the Object constructormight be:

而使用Object 构造函数创建的同一个对象可能是:

var obj = new Object();
obj.foo = 'foo';
obj.bar = 'bar';

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 nodeTypeproperty doesn't mean it's a DOM object.

您可以使用“鸭子打字”,但仅限于有限的范围。例如,您不能仅仅因为一个对象有一个getFullYear()方法就保证它是一个 Date 对象。同样,仅仅因为它有一个nodeType属性并不意味着它是一个 DOM 对象。

For example, the jQuery isPlainObjectfunction thinks that if an object has a nodeType property, it's a DOM node, and if it has a setIntervalproperty it's a Window object. That sort of duck typing is extremely simplistic and will fail in some cases.

例如,jQueryisPlainObject函数认为如果一个对象有一个 nodeType 属性,它就是一个 DOM 节点,如果它有一个setInterval属性,它就是一个 Window 对象。这种鸭子类型非常简单,在某些情况下会失败。

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).

您可能还注意到 jQuery 依赖于以特定顺序返回的属性 - 另一个不受任何标准支持的危险假设(尽管一些支持者试图更改标准以适应他们假设的行为)。

Edit 22-Apr-2014: in version 1.10 jQuery includes a support.ownLastproperty 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 anyorder, regardless of whether they are inherited or own, and may be jumbled.

2014 年 4 月 22 日编辑:在 1.10 版中,jQuery 包含一个基于测试单个属性的support.ownLast属性(显然这是为了 IE9 支持)以查看继承的属性是首先枚举还是最后枚举。这继续忽略一个事实,即对象的属性可以以任何顺序返回,无论它们是继承的还是拥有的,并且可能会混乱。

Probably the simplest test for "plain" objects is:

对“普通”对象最简单的测试可能是:

function isPlainObj(o) {
  return typeof o == 'object' && o.constructor == Object;
}

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 instanceoftest too, but I can't see that it does anything that the constructor test doesn't.

对于使用对象字面量或 Object 构造函数创建的对象,这总是正确的,但很可能会为以其他方式创建的对象提供虚假结果,并且可能(可能会)跨帧失败。你也可以添加一个instanceof测试,但我看不出它做了构造函数测试没有做的任何事情。

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.

如果您传递 ActiveX 对象,最好将其包装在 try..catch 中,因为它们可以返回各种奇怪的结果,甚至抛出错误。

Edit 13-Oct-2015

编辑 2015 年 10 月 13 日

Of course there are some traps:

当然也有一些陷阱:

isPlainObject( {constructor: 'foo'} ); // false, should be true

// In global scope
var constructor = Object;
isPlainObject( this );        // true, should be false

Messing with the constructor property will cause issues. There are other traps too, such as objects created by constructors other than Object.

弄乱构造函数属性会导致问题。还有其他陷阱,例如由 Object 以外的构造函数创建的对象。

Since ES5 is now pretty much ubiquitous, there is Object.getPrototypeOfto 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:

由于 ES5 现在几乎无处不在,因此可以使用Object.getPrototypeOf来检查[[Prototype]]对象的 。如果它是内置 Object.prototype,则该对象是一个普通对象。但是,一些开发人员希望创建没有继承属性的真正“空”对象。这可以使用:

var emptyObj = Object.create(null);

In this case, the [[Prototype]]property is null. So simply checking if the internal prototype is Object.prototypeisn't sufficient.

在这种情况下,该[[Prototype]]属性为null。所以仅仅检查内部原型是否是Object.prototype是不够的。

There is also the reasonably widely used:

还有合理广泛使用的:

Object.prototype.toString.call(valueToTest)

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:

被指定为基于内部[[Class]]属性返回一个字符串,对于 Objects,它是 [object Object]。但是,在 ECMAScript 2015 中这已经改变了,所以测试是针对其他类型的对象执行的,默认是 [object Object],所以这个对象可能不是一个“普通对象”,只是一个不被识别为其他东西的对象。因此,规范指出:

"[testing using toString] does not provide a reliable type testing mechanism for other kinds of built-in or program defined objects."

“[使用 toString 进行测试] 没有为其他类型的内置或程序定义的对象提供可靠的类型测试机制。”

http://www.ecma-international.org/ecma-262/6.0/index.html#sec-object.prototype.tostring

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.

因此,允许使用 ES5 之前的主机、具有[[Prototype]]null 的对象和其他没有getPrototypeOf 的对象类型(例如null,感谢Chris Nielsen)的更新函数如下。

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).

请注意,无法填充getPrototypeOf,因此如果需要支持旧浏览器(例如,根据MDNIE 8 及更低版本),则可能没有用。

/*  Function to test if an object is a plain object, i.e. is constructed
**  by the built-in Object constructor and inherits directly from Object.prototype
**  or null. Some built-in objects pass the test, e.g. Math which is a plain object
**  and some host or exotic objects may pass also.
**
**  @param {} obj - value to test
**  @returns {Boolean} true if passes tests, false otherwise
*/
function isPlainObject(obj) {

  // Basic check for Type object that's not null
  if (typeof obj == 'object' && obj !== null) {

    // If Object.getPrototypeOf supported, use it
    if (typeof Object.getPrototypeOf == 'function') {
      var proto = Object.getPrototypeOf(obj);
      return proto === Object.prototype || proto === null;
    }
    
    // Otherwise, use internal class
    // This should be reliable as if getPrototypeOf not supported, is pre-ES5
    return Object.prototype.toString.call(obj) == '[object Object]';
  }
  
  // Not an object
  return false;
}


// Tests
var data = {
  'Host object': document.createElement('div'),
  'null'       : null,
  'new Object' : {},
  'Object.create(null)' : Object.create(null),
  'Instance of other object' : (function() {function Foo(){};return new Foo()}()),
  'Number primitive ' : 5,
  'String primitive ' : 'P',
  'Number Object' : new Number(6),
  'Built-in Math' : Math
};

Object.keys(data).forEach(function(item) {
  document.write(item + ': ' + isPlainObject(data[item]) + '<br>');
});

回答by Vlada

Similar to @RobG example:

类似于@RobG 示例:

function isPlainObject(obj) {
    return  typeof obj === 'object' // separate from primitives
        && obj !== null         // is obvious
        && obj.constructor === Object // separate instances (Array, DOM, ...)
        && Object.prototype.toString.call(obj) === '[object Object]'; // separate build-in like Math
}

TEST:

测试:

function isPlainObject(obj) {
 return typeof obj === 'object'
  && obj !== null
  && obj.constructor === Object
  && Object.prototype.toString.call(obj) === '[object Object]';
}

var data = {
  '{}': {},
  'DOM element': document.createElement('div'),
  'null'       : null,
  'Object.create(null)' : Object.create(null),
  'Instance of other object' : new (function Foo(){})(),
  'Number primitive ' : 5,
  'String primitive ' : 'P',
  'Number Object' : new Number(6),
  'Built-in Math' : Math
};

Object.keys(data).forEach(function(item) {
  document.write(item + ':<strong>' + isPlainObject(data[item]) + '</strong><br>');
});

回答by standardModel

Since all DOM Nodes inherit from the Node interface you could try the following:

由于所有 DOM 节点都继承自 Node 接口,您可以尝试以下操作:

if(typeof x === 'string') {
    //string
} else if(x instanceof Node) {
    //DOM Node
} else {
    //everything else
}

But I'm not sure if this works in older versions of Internet Explorer

但我不确定这是否适用于旧版本的 Internet Explorer

回答by Anurag

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.

将检查 DOM 节点移至对象文字上方。检查 DOM 节点上存在的某些属性以检测节点。我正在使用nodeType. 这不是万无一失的,因为您可以传入一个对象{nodeType: 0 },这会破坏它。

if (typeof x == 'string') { /* string */ }
else if ('nodeType' in x) { /* dom node */ }
else if (typeof x == 'object') { /* regular object */ }

All duck typing checks like the one above and even instanceofchecks 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.

所有像上面一样的鸭子打字检查,甚至instanceof检查都一定会失败。要真正确定给定对象是否实际上是 DOM 节点,您需要使用传入对象本身以外的其他东西。

回答by Alan Hoffmeister

Maybe something like this?

也许是这样的?

var isPlainObject = function(value){
    if(value && value.toString && value.toString() === '[object Object]')
        return true;

    return false;
};

Or this other approach:

或者另一种方法:

var isObject = function(value){
    var json;

    try {
        json = JSON.stringify(value);
    } catch(e){

    }

    if(!json || json.charAt(0) !== '{' || json.charAt(json.length - 1) !== '}')
        return false;

    return true;
};