3 min read

Correctly checking property exists in JavaScript

While maintaining some code recently I found myself writing out of pure habit Object.hasOwnProperty.call(obj, 'prop') as is expected in almost all OSS I've contributed to, and quickly editing that to just !!obj.prop which I chose because it worked more succinctly in my use case but I generally cringe whenever I read code using that technique. It had me wondering if the OSS project owners who prefer hasOwnProperty over !! actually understand the differences, because I've seen pull requests rejected simply because they used !! and the repo maintainer didn't like the syntax.

I'm going to demonstrate to you 3 ways to check for properties in JavaScript Objects and explain how each can be useful in their own unique way and why hasOwnProperty isn't always the correct option.

Using type coercion

Regarded widely as evil, JavaScript allows us to coerce any value to a boolean using bang bang (!!) which can be troublesome. For the same functionality that allows coercion we have the dilemma that with a truthy value we get the expected true boolean with the type coercion technique, however falsey values will be coerced to a false boolean appropriately, but this isn't useful when simply checking for the existence of an objects property which would also have the expected false, yet the property exists with a falsey value.

var Obj = { foo: 'bar' };
function _hasFoo() {
  return !!this.foo;
}
_hasFoo.call(Obj, 'foo'); // true
Obj.foo = 0;
_hasFoo.call(Obj, 'foo'); // false
Obj.foo = false;
_hasFoo.call(Obj, 'foo'); // false
Obj.foo = undefined;
_hasFoo.call(Obj, 'foo'); // false
delete Obj.foo;
_hasFoo.call(Obj, 'foo'); // false
Obj.foo = true;
_hasFoo.call(Obj, 'foo'); // true

As you can see in the tests the type coercion technique will not appropriately report that the property exists, so type coercion should never be used in that manner. Instead, consider using type coercion only to test for property existence as well as testing the value is set (as a truthy value).

var RedisDAL = Object.create({ 
  canConnect: function(){
    return !!this.endpoint;
  },
  endpoint: ''
});
RedisDAL.canConnect(); // false
RedisDAL.endpoint = 'myclustername.xxx.0001.region.cache.amazonaws.com:port';
RedisDAL.canConnect(); // true

As demonstrated, type coercion can be a succinct solution, if you intend to test for more than just property existence.

The hasOwnProperty method

The accepted standard way to check for property existence in JavaScript, let me demonstrate;

var RedisDAL = { 
  canConnect: function(){
    return Object.hasOwnProperty.call(this, 'endpoint');
  },
  endpoint: ''
};
RedisDAL.canConnect(); // true
RedisDAL.endpoint = false;
RedisDAL.canConnect(); // true
delete RedisDAL.endpoint;
RedisDAL.canConnect(); // false
RedisDAL.endpoint = undefined;
RedisDAL.canConnect(); // true

Whether the value is truthy or otherwise, using hasOwnProperty will consistently report concisely if the object has the property defined.

There is a gotcha, hasOwnProperty cannot read when the property is inherited through the prototypal chain which can be demonstrated using Object.create below;

var RedisDAL = Object.create({ 
  canConnect: function(){
    return Object.hasOwnProperty.call(this, 'endpoint');
  },
  endpoint: ''
});
RedisDAL.canConnect(); // false
RedisDAL.endpoint = false;
RedisDAL.canConnect(); // true
delete RedisDAL.endpoint;
RedisDAL.canConnect(); // false
RedisDAL.endpoint = undefined;
RedisDAL.canConnect(); // true

As you can see with the same tests performed the first test reports false because it could not read the property which RedisDAL inherited through its prototype.

With the in operator

The only way to be 100% sure that the object and its prototypal inherited properties are checked is using the in operator like so;

var RedisDAL = { 
  canConnect: function(){
    return 'endpoint' in this;
  },
  endpoint: ''
};
RedisDAL.canConnect(); // true
RedisDAL.endpoint = false;
RedisDAL.canConnect(); // true
delete RedisDAL.endpoint;
RedisDAL.canConnect(); // false
RedisDAL.endpoint = undefined;
RedisDAL.canConnect(); // true

And now using Object.create to demonstrate inherited properties also;

var RedisDAL = Object.create({ 
  canConnect: function(){
    return 'endpoint' in this;
  },
  endpoint: ''
});
RedisDAL.canConnect(); // true
RedisDAL.endpoint = false;
RedisDAL.canConnect(); // true
delete RedisDAL.endpoint;
RedisDAL.canConnect(); // false
RedisDAL.endpoint = undefined;
RedisDAL.canConnect(); // true

Now we can see that both test scenarios are consistently reporting their results, but this may not be the desired use case. You may not wish to validate inherited properties for good reason.

In short

It should be up to you as a developer to choose the appropriate technique for your particular use case as none are the same and each have specific pro's and con's.
If you document your code well the choice to use one over the others, then there should be no reason to have a Pull Request denied. All 3 are as valid as one another regardless of a maintainers personal opinions or code standards (lack-there-of) and styles.