我如何区分对象字面量其他Javascript对象? [英] How can I differentiate between an object literal other Javascript objects?

查看:266
本文介绍了我如何区分对象字面量其他Javascript对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新:我要重新整理这个问题,因为重要的一点是确定对象字面量:



对象字面量和任何其他Javascript对象(例如DOM节点,Date对象等)之间的差异?我如何写这个函数:

  function f(x){
if(typeof x ==='object literal')
console.log('Object literal!');
else
console.log('Something else!');
}

因此只打印 Object literal! code>作为第一次调用的结果:

  f({name:'Tom'}); 
f(function(){});
f(new String('howdy'));
f('hello');
f(document);






原始问题 / p>

我编写一个Javascript函数,用于接受对象字面量,字符串或DOM节点作为其参数。它需要处理每个参数略有不同,但目前我不知道如何区分一个DOM节点和一个普通的老对象字面量。



这里是一个非常简化的我的函数版本,以及我需要处理的每种参数的测试:

  function f ){
if(typeof x =='string')
console.log('Got a string!');
else if(typeof x =='object')
console.log('Got a object literal!');
else
console.log('Got a DOM node!');
}

f('hello');
f({name:'Tom'});
f(document);

此代码将记录下两次调用的相同消息。我不知道在 else if 子句中要包含什么。我试过像 x instanceof Object 等效果相同的其他变体。



我理解这可能是坏的API /代码设计。即使是,我仍然想知道如何做到这一点。

解决方案


我可以告诉对象字面量和任何其他Javascript对象(例如DOM节点,Date对象等)之间的区别?




$





$ b b

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

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

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

我不认为有任何可靠的方法来区分两个对象



为什么很重要?



一般功能测试策略是测试传递的对象的属性到函数以确定它们是否支持要调用的方法。这样,你不会真正关心对象是如何创建的。



你可以使用鸭式打字,但只有有限的程度。你不能保证只是因为一个对象有一个 getFullYear()方法,它是一个Date对象。同样,只是因为它有一个 nodeType 属性并不意味着它是一个DOM对象。



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



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



编辑22-Apr-2014:在版本1.10 jQuery包括基于测试单个属性(显然这是为了支持IE9)的一个 support.ownLast 属性,以查看继承的属性是列举的第一个还是最后一个。这继续忽略一个事实,即对象的属性可以任何顺序返回,无论它们是继承的还是自己的,并且可能被混杂。



可能简单对象的最简单的测试是:

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

对于使用对象字面量或Object构造函数创建的对象,但是可能会给以其他方式创建的对象提供虚假结果,并且可能(可能会)跨框架失败。您可以添加 instanceof 测试,但我看不到它做任何构造函数测试不。



如果你传递ActiveX对象,最好将它包装在try..catch中,因为它们可以返回各种怪异的结果,甚至会引发错误。



strong> 编辑2015年10月13日



当然还有一些陷阱:

  isPlainObject({constructor:'foo'}); // false,应为true 

//在全局范围
var constructor = Object;
isPlainObject(this); // true,应为false

使用构造函数属性引发问题将导致问题。还有其他陷阱,如由Object以外的构造函数创建的对象。



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

  var emptyObj = Object.create(null); 

在这种情况下, [[Prototype]] 属性为 null 。因此,只需检查内部原型是否为 Object.prototype 是不够的。



还有相当广泛使用的:

  Object.prototype.toString.call(valueToTest)

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


[使用toString测试]不提供可靠的类型测试
机制用于其他类型内置或程序定义的对象。


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



所以一个更新的函数允许前ES5主机,对象与 [[Prototype]] null和其他没有 getPrototypeOf (例如 null 的对象类型, Chris Nielsen )如下。



请注意,没有办法polyfill getPrototypeOf ,因此可能无用如果需要支持旧版浏览器(例如IE 8和更低版本,根据 MDN )。



  / *测试一个对象是否是普通对象的函数,内置Object构造函数并直接从Object.prototype **或null继承。一些内置对象通过测试,例如。数学是一个简单的对象**和一些主机或外来的对象也可以通过**** @param {} obj  - 值测试** @returns {布尔}如果通过测试为真,否则为假* / function isPlainObject obj == null){//如果Object.getPrototypeOf支持,使用它if(typeof Object.getPrototypeOf = ='function'){var proto = Object.getPrototypeOf(obj); return proto === Object.prototype || proto === null; } //否则,使用内部类//这应该是可靠的,如果getPrototypeOf不支持,是pre-ES5返回Object.prototype.toString.call(obj)=='[object Object]'; } //不是对象返回false;} // Testsvar data = {'Host object':document.createElement('div'),'null':null,'new Object':{},'Object.create )':Object.create(null),'其他对象的实例':(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>');});  


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.

解决方案

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

The short answer is you can't.

An object literal is something like:

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

whereas the same object created using the Object constructor might be:

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 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 a setInterval 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:

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 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:

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.

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:

var emptyObj = Object.create(null);

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:

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:

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

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

/*  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>');
});

这篇关于我如何区分对象字面量其他Javascript对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆