Не существует надежного способа отличить простые объекты от классов. Лучшее, что вы можете сделать, это проверить, соответствует ли его прототип прототипу простого объекта. Для полноты вы также можете проверить, является ли прототип null
, поскольку это допустимый простой объект при создании с помощью Object.create(null)
function isPlainObject(obj) {
const prototype = Object.getPrototypeOf(obj);
return prototype === Object.getPrototypeOf({}) ||
prototype === null;
}
class X {
y = 5;
}
let classInstance = new X();
let plainObject = {y: 5};
let noPrototypeObject = Object.create(null);
console.log(isPlainObject(classInstance)); //false
console.log(isPlainObject(plainObject)); //true
console.log(isPlainObject(noPrototypeObject)); //true
Это будет работать до тех пор, пока не будут внесены чрезмерные изменения в прототипы. Если код каким-либо образом вмешивается в них, вы можете получить ложноотрицательные результаты:
function isPlainObject(obj) {
const prototype = Object.getPrototypeOf(obj);
return prototype === Object.getPrototypeOf({}) ||
prototype === null;
}
let plainObject = {y: 5};
let objectCreatedWithNonDefaultPrototype = Object.create({foo: 1});
let objectWithChangedPrototype = Object.create(plainObject);
Object.setPrototypeOf(objectWithChangedPrototype, {bar: 2}) //change the prototype
console.log(isPlainObject(objectCreatedWithNonDefaultPrototype)); //false
console.log(isPlainObject(objectWithChangedPrototype)); //false
или ложных срабатываний:
function isPlainObject(obj) {
const prototype = Object.getPrototypeOf(obj);
return prototype === Object.getPrototypeOf({}) ||
prototype === null;
}
class X {
y = 5;
}
let classInstance = new X();
let classInstanceWithChangedPrototype = new X();
Object.setPrototypeOf(classInstanceWithChangedPrototype, null);
console.log(isPlainObject(classInstance)); //false
console.log(isPlainObject(classInstanceWithChangedPrototype)); //true
Вы также можете проверить свойство constructor
из прототипа. Он может лучше работать для обнаружения obj = Object.create({foo: 1})
, где obj
будет простым объектом с другим простым объектом в качестве прототипа.
function isPlainObject(obj) {
const prototype = Object.getPrototypeOf(obj);
return prototype === null || prototype.constructor === Object;
}
class X {
y = 5;
}
let classInstance = new X();
let plainObject = {y: 5};
let noPrototypeObject = Object.create(null);
let objectCreatedWithNonDefaultPrototype = Object.create({foo: 1});
let objectWithChangedPrototype = Object.create(plainObject);
Object.setPrototypeOf(objectWithChangedPrototype, {bar: 2}) //change the prototype
console.log(isPlainObject(classInstance)); //false
console.log(isPlainObject(plainObject)); //true
console.log(isPlainObject(noPrototypeObject)); //true
console.log(isPlainObject(objectCreatedWithNonDefaultPrototype)); //true
console.log(isPlainObject(objectWithChangedPrototype)); //true
Это все еще не пуленепробиваемое, поскольку конструктор все еще можно переопределить. Однако, скорее всего, это будет код, который активно пытается спрятаться. Большинство объектов должно быть обнаружено любым из двух методов.
Чтобы найти все примитивы и простые объекты, вы можете использовать typeof
, поскольку он сообщает о типах. Обратите внимание, что typeof null
это "object"
, поэтому требуется дополнительная проверка:
function isPlainObject(obj) {
const prototype = Object.getPrototypeOf(obj);
return prototype === null || prototype.constructor === Object;
}
function isPrimitive(x) {
return x === null || (typeof x !== "function" && typeof x !== "object")
}
function filter(x) {
return isPrimitive(x) || isPlainObject(x);
}