2009-07-23 2 views
13

Есть ли способ определить в Javascript, если объект был создан с использованием нотации object-literal или с использованием метода конструктора?Как определить, является ли объект литералом объекта в Javascript?

Мне кажется, что вы просто обращаетесь к его родительскому объекту, но если объект, который вы проходите, не имеет ссылки на его родителя, я не думаю, что вы можете это сказать, не так ли?

+0

jeresig попросил меня реализовать функцию, которая сделает именно это для него. – leeand00

+0

Объект никогда не является объектным литералом, так что вы можете перефразировать вопрос? – Nosredna

+0

И, кстати, какой твой любимый журнал Commodore 64? – Nosredna

ответ

10

Я просто наткнулся на этот вопрос и нити во время сладкого hackfest, который участвует в Грааль поиски для оценки, был ли объект, созданный с {} или new Object() (я все еще не понял.)

В любом случае, я был удивлен, чтобы найти сходство между функцией isObjectLiteral(), размещенной здесь, и моей собственной функцией isObjLiteral(), которую я написал для проекта Pollen.JS , Я считаю, что это решение было опубликовано до моего опроса Pollen.JS, поэтому - шляпы от вас! Поверхность к моей - это длина ... меньше половины (когда включена ваша рутина настройки), но обе дают одинаковые результаты.

Посмотрите:

 
function isObjLiteral(_obj) { 
    var _test = _obj; 
    return ( typeof _obj !== 'object' || _obj === null ? 
       false : 
       (
       (function() { 
        while (!false) { 
        if ( Object.getPrototypeOf(_test = Object.getPrototypeOf(_test) ) === null) { 
         break; 
        }  
        } 
        return Object.getPrototypeOf(_obj) === _test; 
       })() 
      ) 
     ); 
} 

Кроме того, некоторые тестовое вещество:

 
var _cases= { 
    _objLit : {}, 
    _objNew : new Object(), 
    _function : new Function(), 
    _array : new Array(), 
    _string : new String(), 
    _image : new Image(), 
    _bool: true 
}; 

console.dir(_cases); 

for (var _test in _cases) { 
    console.group(_test); 
    console.dir({ 
    type: typeof _cases[_test], 
    string: _cases[_test].toString(), 
    result: isObjLiteral(_cases[_test]) 
    });  
    console.groupEnd(); 
} 

Или на jsbin.com ...

http://jsbin.com/iwuwa

Обязательно откройте поджигатель когда вы туда доберетесь - отладка в документе предназначена для любителей IE.

+0

@Rick, это решение кажется более элегантным. – leeand00

+0

@ leeand00: Это примерно такая же длина, как у меня. Я просто включил реализацию Object.getPrototypeOf в мой тоже, который этот __doesn't__ включает. –

+0

Кроме того, если вы удаляете часть 'Object.getPrototypeOf.isNative', моя только 6 SLOC. –

3

Невозможно рассказать о различии между объектом, построенным из литерала объекта, и одним, построенным из других средств.

Это немного похоже на вопрос, можете ли вы определить, была ли построена числовая переменная, присвоив значение «2» или «3-1»;

Если вам нужно это сделать, вам нужно будет поместить некоторую конкретную подпись в свой литерал объекта для обнаружения позже.

3

Объектный литерал - это обозначение, которое вы используете для определения объекта - который в javascript всегда находится в форме пары имя-значение, окруженной фигурными скобками. Как только это было выполнено, невозможно определить, был ли объект создан с помощью этих обозначений или нет (на самом деле, я думаю, это может быть чрезмерное упрощение, но в основном правильное). У вас просто есть объект. Это одна из замечательных особенностей js в том, что есть много коротких сокращений, чтобы делать то, что может быть намного длиннее, чтобы писать. Короче говоря, буквальное обозначение заменяет необходимости писать:

var myobject = new Object(); 
3

Похоже, что вы ищете это:

function Foo() {} 

var a = {}; 
var b = new Foo(); 

console.log(a.constructor == Object); // true 
console.log(b.constructor == Object); // false 

Конструктор собственности на объект является указателем на функцию, которая используется для постройте его. В приведенном выше примере b.constructor == Foo. Если объект был создан с использованием фигурных скобок (литеральная запись массива) или с использованием new Object(), то его свойство конструктора будет == Object.

Обновление: crescentfresh указал, что $(document).constructor == Object вместо того, чтобы быть равным конструктору jQuery, поэтому я сделал немного больше копания. Кажется, что с помощью литерала объекта в качестве прототипа объекта вы оказываете свойство конструктора практически бесполезное:

function Foo() {} 
var obj = new Foo(); 
obj.constructor == Object; // false 

но:

function Foo() {} 
Foo.prototype = { objectLiteral: true }; 
var obj = new Foo(); 
obj.constructor == Object; // true 

Там есть очень хорошее объяснение этого в другом ответе here , и более активное объяснение here.

Я думаю, что другие ответы правильные, и на самом деле нет способа обнаружить это.

+1

'constructor === Object' true для множества объектов. Например, попробуйте 'javascript: alert ($ (document) .constructor === Object)' на этой странице, даже если 'jQuery! == Object'. –

+0

Я обновил свой ответ на основе комментария crescentfresh. – Prestaul

11

Edit: Я интерпретации «литерал объекта», как и все созданные с помощью литерала объекта илиObject конструктор. Это то, что, вероятно, имел в виду Джон Ресиг.

У меня есть функция, которая будет работать, даже если .constructor был испорчен или если объект был создан в другом фрейме. Обратите внимание, что Object.prototype.toString.call(obj) === "[object Object]" (как полагают некоторые, не решит эту проблему).

function isObjectLiteral(obj) { 
    if (typeof obj !== "object" || obj === null) 
     return false; 

    var hasOwnProp = Object.prototype.hasOwnProperty, 
    ObjProto = obj; 

    // get obj's Object constructor's prototype 
    while (Object.getPrototypeOf(ObjProto = Object.getPrototypeOf(ObjProto)) !== null); 

    if (!Object.getPrototypeOf.isNative) // workaround if non-native Object.getPrototypeOf 
     for (var prop in obj) 
      if (!hasOwnProp.call(obj, prop) && !hasOwnProp.call(ObjProto, prop)) // inherited elsewhere 
       return false; 

    return Object.getPrototypeOf(obj) === ObjProto; 
}; 


if (!Object.getPrototypeOf) { 
    if (typeof ({}).__proto__ === "object") { 
     Object.getPrototypeOf = function (obj) { 
      return obj.__proto__; 
     }; 
     Object.getPrototypeOf.isNative = true; 
    } else { 
     Object.getPrototypeOf = function (obj) { 
      var constructor = obj.constructor, 
      oldConstructor; 
      if (Object.prototype.hasOwnProperty.call(obj, "constructor")) { 
       oldConstructor = constructor; 
       if (!(delete obj.constructor)) // reset constructor 
        return null; // can't delete obj.constructor, return null 
       constructor = obj.constructor; // get real constructor 
       obj.constructor = oldConstructor; // restore constructor 
      } 
      return constructor ? constructor.prototype : null; // needed for IE 
     }; 
     Object.getPrototypeOf.isNative = false; 
    } 
} else Object.getPrototypeOf.isNative = true; 

Вот HTML для TestCase:

<!DOCTYPE html> 
<html> 
<head> 
    <meta charset="utf-8"/> 
    <!-- Online here: http://code.eligrey.com/testcases/all/isObjectLiteral.html --> 
    <title>isObjectLiteral</title> 
    <style type="text/css"> 
    li { background: green; } li.FAIL { background: red; } 
    iframe { display: none; } 
    </style> 
</head> 
<body> 
<ul id="results"></ul> 
<script type="text/javascript"> 
function isObjectLiteral(obj) { 
    if (typeof obj !== "object" || obj === null) 
     return false; 

    var hasOwnProp = Object.prototype.hasOwnProperty, 
    ObjProto = obj; 

    // get obj's Object constructor's prototype 
    while (Object.getPrototypeOf(ObjProto = Object.getPrototypeOf(ObjProto)) !== null); 

    if (!Object.getPrototypeOf.isNative) // workaround if non-native Object.getPrototypeOf 
     for (var prop in obj) 
      if (!hasOwnProp.call(obj, prop) && !hasOwnProp.call(ObjProto, prop)) // inherited elsewhere 
       return false; 

    return Object.getPrototypeOf(obj) === ObjProto; 
}; 


if (!Object.getPrototypeOf) { 
    if (typeof ({}).__proto__ === "object") { 
     Object.getPrototypeOf = function (obj) { 
      return obj.__proto__; 
     }; 
     Object.getPrototypeOf.isNative = true; 
    } else { 
     Object.getPrototypeOf = function (obj) { 
      var constructor = obj.constructor, 
      oldConstructor; 
      if (Object.prototype.hasOwnProperty.call(obj, "constructor")) { 
       oldConstructor = constructor; 
       if (!(delete obj.constructor)) // reset constructor 
        return null; // can't delete obj.constructor, return null 
       constructor = obj.constructor; // get real constructor 
       obj.constructor = oldConstructor; // restore constructor 
      } 
      return constructor ? constructor.prototype : null; // needed for IE 
     }; 
     Object.getPrototypeOf.isNative = false; 
    } 
} else Object.getPrototypeOf.isNative = true; 

// Function serialization is not permitted 
// Does not work across all browsers 
Function.prototype.toString = function(){}; 

// The use case that we want to match 
log("{}", {}, true); 

// Instantiated objects shouldn't be matched 
log("new Date", new Date, false); 

var fn = function(){}; 

// Makes the function a little more realistic 
// (and harder to detect, incidentally) 
fn.prototype = {someMethod: function(){}}; 

// Functions shouldn't be matched 
log("fn", fn, false); 

// Again, instantiated objects shouldn't be matched 
log("new fn", new fn, false); 

var fn2 = function(){}; 

log("new fn2", new fn2, false); 

var fn3 = function(){}; 

fn3.prototype = {}; // impossible to detect (?) without native Object.getPrototypeOf 

log("new fn3 (only passes with native Object.getPrototypeOf)", new fn3, false); 

log("null", null, false); 

log("undefined", undefined, false); 


/* Note: 
* The restriction against instantiated functions is 
* due to the fact that this method will be used for 
* deep-cloning an object. Instantiated objects will 
* just have their reference copied over, whereas 
* plain objects will need to be completely cloned. 
*/ 

var iframe = document.createElement("iframe"); 
document.body.appendChild(iframe); 

var doc = iframe.contentDocument || iframe.contentWindow.document; 
doc.open(); 
doc.write("<body onload='window.top.iframeDone(Object);'>"); 
doc.close(); 

function iframeDone(otherObject){ 
    // Objects from other windows should be matched 
    log("new otherObject", new otherObject, true); 
} 

function log(msg, a, b) { 
    var pass = isObjectLiteral(a) === b ? "PASS" : "FAIL"; 

    document.getElementById("results").innerHTML += 
    "<li class='" + pass + "'>" + msg + "</li>"; 
} 


</script> 
</body> 
</html> 
+1

Отличный ответ, и приятно иметь вас в stackoverflow. Off, но также спасибо за методы массива e4x. – Prestaul

+0

Теперь, когда я прочитал ваш ответ, я ** думаю ** Я понимаю вопрос. – tvanfosson

+1

Whoa! Что такое знак равенства 3x (===)? – leeand00

2

Я была такая же проблема, так что я решил пойти по этому пути:

function isPlainObject(val) { 
    return val ? val.constructor === {}.constructor : false; 
} 
// Examples: 
isPlainObject({}); // true 
isPlainObject([]); // false 
isPlainObject(new Human("Erik", 25)); // false 
isPlainObject(new Date); // false 
isPlainObject(new RegExp); // false 
//and so on... 
+0

Насколько надежна эта функция? –

+0

@Quentin Engles, почему бы и нет? Я протестировал его в разных браузерах, попробуйте запустить его самостоятельно. – a8m

+0

Как и когда для свойства конструктора задано Object на объекте с конструктором. Опять же, если бы все желающие хотели сказать, было ли что-то буквальным, а если нет, тогда я не знаю. –

0

что вы хотите:

Object.getPrototypeOf(obj) === Object.prototype 

Это проверяет, что объект является простым объектом, созданным с любым new Object() или {...}, а не подкласса Object.

Смежные вопросы