An object unlike others: process.env
Yesterday I found myself playing with a configuration hierarchy. Anything set in the environment would override anything defined by a defaults.json file which would override anything hardcoded into the application. My initial attempt led me to discover some interesting things about node's process.env object. Without further ado:
console.log(process.env.hasOwnProperty('PROPERTY_NAME'));
Any guesses what the output will be?
With node 0.6.10 I get:
TypeError: Property 'hasOwnProperty' of object #<error> is not a function
Not what I was expecting. What about a different test:
console.log('hasOwnProperty' in process.env); // output -> true console.log(process.env.hasOwnProperty); // output -> undefined
Fun. I've not look at the node source but given that the environment could define a property called hasOwnProperty it makes sense that process.env masks the standard prototype model even if the in operator exposes it.
Besides being a fun exercise playing with node, why does this matter you might ask? What started this all was detecting if an environment variable existed but was set to the empty string. The standard if check fails:
// This simulates launching node like: EMPTY= node env.js process.env.EMPTY = ''; if (process.env['EMPTY']) { console.log('EXISTS'); } else { console.log('DOES NOT EXIST'); } // output -> DOES NOT EXIST
Since the empty string is falsy in JavaScript this doesn't work. As we saw above doing a hasOwnProperty check won't work and the in operator could give false positives.
Thinking outside the box, given that the source of the values in process.env is the environment node was launched from, we know that the environment can't set a property value to be undefined. The simple existence check then is to see if the value is undefined:
console.log(typeof process.env['EMPTY'] !== 'undefined');
Or just use underscore's isUndefined method since you should be using that library anyway.
UPADTE
Just because we can't call hasOwnProperty() directly on process.env doesn't mean we can't still use it. Instead we just need to call it indirectly:
console.log(Object.prototype.hasOwnProperty.call(process.env, 'EMPTY')); // output -> false process.env.EMPTY = undefined; console.log(Object.prototype.hasOwnProperty.call(process.env, 'EMPTY')); // output -> true process.env.EMPTY = ''; console.log(Object.prototype.hasOwnProperty.call(process.env, 'EMPTY')); // output -> true