Interviewer: “5 years, and still struggling with key existence checks in objects?”
The Pitfalls of ‘in’ and ‘hasOwnProperty’ in JavaScript
Well, this is not really an interview question but a concept that every web developer should know.
In JavaScript we use objects a lot, but when it comes it checking if a certain key exists in an object or not, if we use the simplest and quickest possible way to do the job, without knowing the consequences.
For frontend applications it might be forgiving but for backend application developed in node js, it can cause severe damange if any attacker find out that vulernability.
In this article we will be seeing
- what are the available ways to check if certain property exists in an object or not
- their shortcomings
- in the last what is be best possible way.
Way 1: Using Object.keys()
const obj = {};
const keyToFind = 'data';
if(Object.keys(obj).includes(keyToFind)) {
console.log('Key Found');
} else {
console.log('Key Not Found 404');
}
// This will output Key Not Found 404
This is what we want.
Drawback:
But wait, what If we define a key using Object.defineProperty
const obj = {};
Object.defineProperty('data', {value:true});
const keyToFind = 'data';
if(Object.keys(obj).includes(keyToFind)) {
console.log('Key Found');
} else {
console.log('Key Not Found 404');
}
// This will output Key Found
Wait What?
Yes, because keys added via Object.defineProperty
are non-enumerable by default, and Object.keys
method only show keys that are enumerable.
So we can’t rely on this method.
Way 2: Using in operator
const obj = {};
const keyToFind = 'data';
if(keyToFind in obj) {
console.log('Key Found');
} else {
console.log('Key Not Found 404');
}
// This will output Key Not Found 404
This is what we want.
Drawback:
But wait, what If the key which we have to check is constructor
const obj = {};
const keyToFind = 'constructor';
if(keyToFind in obj) {
console.log('Key Found');
} else {
console.log('Key Not Found 404');
}
// This will output Key Found
Wait What?
Yes because in
operator searches for key in prototype chain, and guess what, we have constructor
key available in prototype chain of each object.
Way 3: Using hasOwnProperty
hasOwnProperty solves the problem of prototype chain
lookup, but it has it’s own drawbacks.
const obj = {};
const keyToFind = 'data';
if(obj.hasOwnProperty(keyToFind)) {
console.log('Key Found');
} else {
console.log('Key Not Found 404');
}
// This will output Key Not Found 404
This is what we want.
Drawback 1:
But wait, what If the object in which we have to check presence of key is created from null
.
const obj = Object.create(null);
const keyToFind = 'data';
if(obj.hasOwnProperty(keyToFind)) {
console.log('Key Found');
} else {
console.log('Key Not Found 404');
}
// This will output Key Not Found 404
This will give us
VM2732:4 Uncaught TypeError: obj.hasOwnProperty is not a function
at <anonymous>:4:8
Because we have created object from null
, we have cleared the prototype chain and now there are no inbuilt method attached to this object.
Drawback 2:
We can override hasownproperty
.
const obj = {};
obj.hasOwnProperty = ()=> true;
const keyToFind = 'data';
if(obj.hasOwnProperty(keyToFind)) {
console.log('Key Found');
} else {
console.log('Key Not Found 404');
}
// This will output Key Found
Wait What?
Yes because we have implemented our own buggy version.
So What is the solution?
Object.hasOwn(Object, KeyToCheck);
Here comes Object.hasOwn to the rescue, Object.hasOwn is a static method that exists ob object and can be used to resolve all the problems which we have discussed above.
It comes as a part of ES2022. And is supported in all latest browsers.
const obj = {};
const keyToFind = 'data';
if(Object.hasOwn(obj, keyToFind)) {
console.log('Key Found');
} else {
console.log('Key Not Found 404');
}
// O/P Key Not Found 404
With constructor
Key:
const obj = {};
const keyToFind = 'constructor';
if(Object.hasOwn(obj, keyToFind)) {
console.log('Key Found');
} else {
console.log('Key Not Found 404');
}
// O/P Key Not Found 404
With Object created from null:
const obj = Object.create(null);
const keyToFind = 'constructor';
if(Object.hasOwn(obj, keyToFind)) {
console.log('Key Found');
} else {
console.log('Key Not Found 404');
}
// O/P Key Not Found 404
With property added via Object.defineProperty.
const obj = {};
Object.defineProperty(obj,'data', {value:true});
const keyToFind = 'data';
if(Object.hasOwn(obj, keyToFind)) {
console.log('Key Found');
} else {
console.log('Key Not Found 404');
}
// This will output Key Found
Thanks for Reading, I hope this made you learn something new.