Как определить, является ли объект литералом объекта в Javascript?

Есть ли способ определить в Javascript, был ли объект создан с помощью литеральной нотации объекта или с помощью метода конструктора?

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

10 ответов

Решение

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

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

Взглянуть:

function isObjLiteral (_obj) {
  var _test = _obj;
  return (typeof _obj! == 'object' || _obj === null?
              ложный:  
              (
                (function () {
                  while (!false) {
                    if (  Object.getPrototypeOf( _test = Object.getPrototypeOf(_test)) === null) {
                      перерыв;
                    }      
                  }
                  return Object.getPrototypeOf(_obj) === _test;
                })()));
}

Кроме того, некоторые тестовые материалы:

var _cases = {
    _objLit: {}, 
    _objNew: новый объект (),
    _function: new Function(),
    _array: new Array(), 
    _string: новая строка (),
    _image: новое изображение (),
    _bool: правда
};

console.dir (_cases);

for (var _test in _cases) {
  console.group (_test);
  console.dir ({
    type: typeof _cases [_test], 
    строка:  _cases[_test].toString(), 
    результат:  isObjLiteral(_cases[_test])  
  });    
  console.groupEnd();
}

Или на jsbin.com...

http://jsbin.com/iwuwa

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

То, что вы хотите, это:

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

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

Редактировать: я интерпретирую "литерал объекта" как все, что создано с использованием литерала объекта или 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-код для теста:

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

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

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

Это очень хорошее объяснение в другом ответе здесь, и более сложное объяснение здесь.

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

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

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

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

var myobject = new Object();

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

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

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

В настоящее время есть более элегантное решение, которое точно отвечает на ваш вопрос:

function isObject(value) {
  return value !== null && value !== undefined && Object.is(value.constructor, Object)
}

// Test stuff below //

class MyClass extends Object {
  constructor(args) {
    super(args)
  }
  say() {
    console.log('hello')
  }
}

function MyProto() {
  Object.call(this)
}
MyProto.prototype = Object.assign(Object.create(Object.prototype), {

  constructor: MyProto,

  say: function() {
    console.log('hello')
  }

});

const testsCases = {
  objectLiteral: {},
  objectFromNew: new Object(),
  null: null,
  undefined: undefined,
  number: 123,
  function: new Function(),
  array: new Array([1, 2, 3]),
  string: new String('foobar'),
  image: new Image(),
  bool: true,
  error: new Error('oups'),
  myClass: new MyClass(),
  myProto: new MyProto()
}

for (const [key, value] of Object.entries(testsCases)) {
  console.log(`${key.padEnd(15)} => ${isObject(value)}`)
}

С наилучшими пожеланиями

      typeof obj === 'object' && obj !== null && Object.getPrototypeOf(obj) === Object.prototype

ниже все возвращают false

      123
null
undefined
'abc'
false
true
[]
new Number()
new Boolean()
() => {}
function () {}

улучшение по сравнению с ответом Джесси

Вопрос 11-летней давности - это мое аккуратное решение, открытое для предложений крайних случаев; шаги -> искать объекты только затем сравнивать, чтобы проверить свойства -> литералы объектов не имеют длины, прототипа и строковых свойств крайнего случая.

попробовал в тесте для JSON и Object.create(Object.create({cool: "joes"})).

Другие вопросы по тегам