JSON.stringify
Using JSON.stringify converts an object to its JSON string representation.
const sampleObj = {};
console.log(JSON.stringify(sampleObj) === '{}'); // true
Limitation: Properteis with values of undefined, functions, or Symbols are omitted during serialization (when in objects) or converted to null (when in arrays).
const testObj = {
x: undefined,
y: function() {},
z: Symbol('test')
};
console.log(JSON.stringify(testObj) === '{}'); // true
for...in Loop with hasOwnProperty
A for...in loop iterates over enumerable properties, including thosee from the prototype chain.
const myObj = {};
Object.prototype.inheritedProp = 'value';
function checkIfEmpty(object) {
let isEmpty = true;
for (let prop in object) {
isEmpty = false;
break;
}
return isEmpty;
}
console.log(checkIfEmpty(myObj)); // false
To check only the object's own properties, use hasOwnProperty.
function checkIfEmptyOwn(object) {
let isEmpty = true;
for (let prop in object) {
if (object.hasOwnProperty(prop)) {
isEmpty = false;
break;
}
}
return isEmpty;
}
console.log(checkIfEmptyOwn(myObj)); // true
Limitation: for...in does not iterate over non-enumerable properties.
Object.keys
Object.keys returns an array of an object's own enumerable property names.
const anotherObj = {};
Object.prototype.someProp = 123;
console.log(Object.keys(anotherObj).length === 0); // true
Limitation: Like for...in, it only accesses enumerable properties.
const definedObj = {};
Object.defineProperty(definedObj, 'hidden', {
value: 'secret',
enumerable: false
});
console.log(definedObj.hidden); // 'secret'
console.log(Object.keys(definedObj).length === 0); // true
Object.getOwnPropertyNames
Object.getOwnPropertyNames returns an array of all own property names, including non-enumerable ones.
const objWithNonEnum = {};
Object.defineProperty(objWithNonEnum, 'data', {
value: 42,
enumerable: false
});
console.log(Object.getOwnPropertyNames(objWithNonEnum)); // [ 'data' ]
Limitation: It does not include properties keyed by Symbols.
const symKey = Symbol('id');
const symbolObj = { [symKey]: 100 };
console.log(symbolObj); // { [Symbol(id)]: 100 }
console.log(Object.getOwnPropertyNames(symbolObj).length === 0); // true
console.log(JSON.stringify(symbolObj) === '{}'); // true
console.log(Object.keys(symbolObj).length === 0); // true
Combining Object.getOwnPropertyNames and Object.getOwnPropertySymbols
Object.getOwnPropertySymbols retrieves only Symbol-keyed properties. Combining both methods proivdes a comprehensive list.
const sym = Symbol('flag');
const case1 = { [sym]: true };
const case2 = { name: 'test' };
const case3 = {};
Object.defineProperty(case3, 'internal', { value: 0, enumerable: false });
const case4 = {};
function countProperties(obj) {
return Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)).length;
}
console.log(countProperties(case1) === 0); // false
console.log(countProperties(case2) === 0); // false
console.log(countProperties(case3) === 0); // false
console.log(countProperties(case4) === 0); // true
Reflect.ownKeys
Reflect.ownKeys returns an array of all own property keys, including strings, Symbols, and non-enumerable properties. It is equivalent to the combined approach above.
const symbolKey = Symbol('ref');
const objA = { [symbolKey]: 'value' };
const objB = { prop: 1 };
const objC = {};
Object.defineProperty(objC, 'private', { value: 'x', enumerable: false });
const objD = {};
console.log(Reflect.ownKeys(objA).length === 0); // false
console.log(Reflect.ownKeys(objB).length === 0); // false
console.log(Reflect.ownKeys(objC).length === 0); // false
console.log(Reflect.ownKeys(objD).length === 0); // true
For a robust check of an empty object, Reflect.ownKeys is the most complete method.