First a little bit of context (you can skip this part if you prefer to focus on code).
I joined a new project that will be integrated in a nodeJS's platform. Our team does have experience with JEE enterprise Web Apps. That sets up our background. This new project will consume REST APIs, aggregate some data, implement some business logic and pass these data to front-end consumer. Sort of lightweight microservice architecture.
Some co-workers started to work on the project and I found it that in the source code we do have a lot of code snippet like if (foo != null && foo.property != null) {return foo.property.value;}
Foo being supposed to be an object passed as an argument to a function which would implement that kind of test.
A snippet example will talk more.
Let's pretend that's the response of an API i am consuming. I want to write a small function which would return statusValue if the object does exist, if it's not null or undefined, not empty and the property does exist and isn't blank or empty for instance.
var response = {
"services":[],
"metadata": {
"status": "statusValue"
}
};
That's how it is as for now :
function getStatusValue(response) {
if (response != null && response.metadata != null) {
return response.metadata.status;
}
};
What would be considered as a best JS practise to implement that (we are also using lodash so maybe it's a better option to use lodash internals for that). I am just fearing we are transposing our Java habbits.
Basically i would like to know how to check for null, undefined, empty, blank safely (if that makes sense). What would be the JS best practice for that in 2016 with libraries such as lodash and stuff.
When checking for properties of an object, checking for loose equality to null is not enough.
Loose equality (==) employs type hinting in JavaScript, which attempts converting the members to a common type that can then be used for determining whether they are equal or not.
As such, best practices for JavaScript dictate that strict equality (===) is always to be used, in order to avoid edge-case scenarios or unknown behavior when checking for values or types.
You can find more info about loose vs strict equality here:
https://developer.mozilla.org/en/docs/Web/JavaScript/Equality_comparisons_and_sameness
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators
While this is not a must-have in your function, it is a good practice to follow and develop as a habit, so that on later code the implications of loose equality (==) could be avoided (e.g. avoiding '3' == 3, when a number or string type is expected).
Although considered a downside of the language by some, using null is similar in intent as checking for undefined, but is actually meant to express in the code that the coder is expecting an object (or lack of) to be provided, instead of a primitive type (number, string, boolean, function). This differs from Java, or any other typed language, where null is used for Object type defined variables; but in JavaScript there's no constraining a variable to given type.
You can find out more details about null at MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null
In practice, and especially when dealing with values that come outside of a unit - which is the case of any API - is considered a best practice to actually check for the type being an object, in order to make sure it has properties.
While your code is not incorrect, it does show that there's not much attention to best practices, which eventually will lead to writing buggy code.
My suggestion for your code, following best practices and independent of any library, is:
function getStatusValue(response) {
// must be of type `object`, to have properties.
if (typeof response === 'object' &&
// any variable of type `object` might be `null`, so exclude that.
response !== null &&
// `metadata` property must be of type `object`, to have properties.
typeof response.metadata !== 'object' &&
// `metadata` is of type `object`, check for not being `null`.
response.metadata !== null) {
// `status` property is expected to be a string, given by the API spec
if (typeof response.metadata.status === 'string' &&
// on strings, we can access the `length` property to check if empty string (0 chars).
response.metadata.status.length > 0) {
// all good, return `status` property.
return response.metadata.status;
} else {
// `status` property is either not a string, or an empty string ('').
}
} else {
// invalid `response` object.
}
}
Also, it might be easier to create or use from any libraries you integrate, a function to check for a valid object; something like _.isObject or this:
function isObject(o) {
return (typeof o === 'object' && o !== null);
}
which you could later be used in the above snipped like so:
function getStatusValue(response) {
if (isObject(response) && isObject(response.metadata)) {
if (typeof response.metadata.status === 'string' &&
response.metadata.status.length > 0) {
return response.metadata.status;
} else {
// `status` property is either not a string, or an empty string ('').
}
} else {
// invalid `response` object.
}
}
As a final thought, as it is clearly visible, following best practices does increase the size of your code, but the benefit is that the resulting code is much safer, with less chances of throwing exceptions (root of many app crashes), easier to collaborate on, and easier to build tests/coverage for.
Using Lodash, you can use the _.isNil(response) function documented here.
In general, I would check a variable like this:
if (typeof x !== 'undefined' && x !== null) {
// x has some value
}
You should not check whether x === undefined because if x is undefined, you'll get a dereference error.
I would implement this using the following function:
var response = {
"services":[],
"metadata": {
"status": "statusValue"
}
};
function getStatusValue(res) {
if (res && res.metadata)
return res.metadata.status || 'not found';
else
return 'not found';
}
console.log(getStatusValue(response));
The if statement will return false if res or res.metadata are undefined and if they both exist, it will return res.metadata.status only if it's defined or 0, otherwise it'll return 'not found'.
Here is a (more concise) way to do such a thing with just JS:
var obj = ...;
var val = obj && obj.prop && obj.prop.val;
val will be undefined/null if either obj or obj.prop are; otherwise it will be obj.prop.val.
This works because of the short-circuit evaluation of &&. It's literal behavior is this: if the first operand is falsey, it evaluates to the first operand. Otherwise, it evaluates to the second. This works as expected for booleans, but as you can see, it can be useful with objects.
|| has similar behavior. For example, you can use it to get a value, or a default if the value is falsey (e.g. null):
var val2 = val || "not found";
Note that this would evaluate to "not found" if val is anything falsey, including 0 or "".
Related
I've always maintained the practice of checking if a value is undefined using
if (typeof x === 'undefined')
However, a colleague is suggesting that using if (x) { is better.
Is there any difference between these two methods from a computational point of view?
There are at least two differences off the top of my mind:
Checking the type as undefined only checks for undefined, unlike if(x), which checks for any truthy values (e.g. true, a non-empty string, a non-zero number, etc)
You can perform typeof on non-existent variables, even in strict mode. You'll get a reference error if you never declared x and did if(x)
"use strict";
const a = undefined;
const b = "truthy value";
if(a) {
console.log("a in if"); // never executes
}
if(typeof a !== "undefined") {
console.log("a with typeof"); // never executes
}
if(b) {
console.log("b in if"); // executes
}
if(typeof b === "undefined") {
console.log("b with typeof"); // never executes
}
try {
if(c) console.log("this should error");
} catch(e) {
console.log("Can't access non-existent variable");
}
console.log("No error:", typeof c);
When should I use which one?
Generally:
Use if(x) when...
You're checking for a boolean
You're checking for (not) 0
You're checking for a non-empty empty string (probably use if(string.length) instead)
Checking the return value of a function (e.g. a function returns null when there's no result for a query or an object when there is (DOM functions like document.getElementById return null when no element with that ID exists))
Use if(typeof x !== "undefined") when...
You're checking whether an object key exists (if(typeof obj.key !== "undefined")) (the proper way as a commentator pointed out is with Object.hasOwn(obj, "key"))
You're checking whether a variable exists (not sure when or why you would do that though)
Checking whether an argument has been passed
Other uses like when you're writing an Express server and checking user-provided content
Something else I probably forgot...
Something that's useful to keep in mind is that Javascript is dynamically typed, even if it looks like it's just variables. There are a handful of types in JS, and Undefined (with caps) is one, and the only value it can hold is undefined. Think of it like saying you have the Number type and it only accepts 42. This is important to know because JS engines have to observe spec.
The Undefined type has exactly one value, called undefined. Any variable that has not been assigned a value has the value undefined.
There is a lot of variation in application code, but you can know that variables that have not been assigned are of type Undefined with value undefined and not something else. Next to Undefined is Null, which has a single value, null. Null, unlike Undefined, needs to be assigned.
In the spec you'll also the find the table for the result you get for each variable type.
You'll notice that Undefined has its own return value, as well as Boolean, but Null returns "object", which is reported to be a mistake in the original spec that we can't get rid of.
With types out of the way, we get to Boolean coercion, which is how if statements work out the condition. The spec has a table of cases that define when a value should be coerced into true or false.
You'll see that an if clause receives the Undefined type, it returns false. The same happens with Null. There are a few other cases that can also return false even if they are not Undefined or Null.
The Number type with values 0, -0, NaN
The String type with length 0 ("")
The BigInt type with value 0
As others have already answered, applications of the spec come in a few different places. The reasons as for why typeof exists and there is an overlap with the falsey evaluation arise from how the JS engines handle values and perform coercion into Boolean.
Consider an illustrative example:
function addSafely(weakset, value) {
if (canBeAddedToWeakSet(value)) {
weakset.add(value);
}
}
So, what do you think is a concise and correct way to implement canBeAddedToWeakSet function in this case? Performance is a nice to have too.
I use the following implementation right now but I'm not sure if it is a complete one (i.e. covers all cases):
function canBeAddedToWeakSet(value) {
return !!value && (typeof value === 'object' || typeof value === 'function');
}
UPD: So, here are some benchmark results for the ones who are curious about the performance.
https://measurethat.net/Benchmarks/ShowResult/6648 (Chrome 61, Xubuntu 16.04)
https://measurethat.net/Benchmarks/ShowResult/6649 (Firefox 52.3 ESR, Xubuntu 16.04)
Refer to ECMAScript 2015 (6th Edition, ECMA-262) WeakSet.add()
23.4.3.1 WeakSet.prototype.add ( value )
The following steps are taken:
Let S be the this value.
If Type(S) is not Object, throw a TypeError exception.
If S does not have a [[WeakSetData]] internal slot, throw a TypeError exception.
If Type(value) is not Object, throw a TypeError exception.
Let entries be the List that is the value of S’s [[WeakSetData]] internal slot.
Repeat for each e that is an element of entries,
If e is not empty and SameValue(e, value) is true, then
Return S.
Append value as the last element of entries.
Return S.
As item 4 said, it checks the type (ECMAScript Language Types) of value is Object or not, because typeof null is 'object' too, so function canBeAddedToWeakSet should be:
function canBeAddedToWeakSet(value) {
return value !== null && (typeof value === 'object' || typeof value === 'function');
}
According to Table 35 of the ECMAScript Specification, Section 12.5.5.1, the typeof operator is allowed to return implementation-dependent strings for Host objects, which are Type Object, and although implementations are encouraged to return "object" in those cases, they are not required to do so.
Implementations are discouraged from defining new typeof result values for non-standard exotic objects. If possible "object" should be used for such objects.
An interesting exception to Table 35 is the case of document.all. The willful violation of the specification was documented with this reasoning:
This violation is motivated by a desire for compatibility with two classes of legacy content: one that uses the presence of document.all as a way to detect legacy user agents, and one that only supports those legacy user agents and uses the document.all object without testing for its presence first.
My recommendation would be to verify that the typeof value does not return the subset of strings that are guaranteed to not satisfy Type(value) of Object, and optionally check for the existence of document.all:
function canBeAddedToWeakSet(value) {
switch (typeof value) {
case 'undefined':
return value !== undefined
case 'boolean':
case 'number':
case 'string':
case 'symbol':
return false
case 'object':
return value !== null
default:
return true
}
}
Disclaimer: This is micro-optimization. Performance is the enemy to every other quality. In some situations it needs to be prioritized, but most of the time, you should prioritize simplicity + readability.
If you really need to sanity check before inserting into WeakSet...
The specs for WeakSet say that only an Object can be inserted into a WeakSet. So the question is really: "How do you check if a value is an Object or not?".
Following the implementation for es6.weak-set.js in the core-js library (which babel-polyfill uses), there's a transitive reference to _is-object.js, which checks if a value is an object or not:
module.exports = function (it) {
return typeof it === 'object' ? it !== null : typeof it === 'function';
};
So, rest assured, your canBeAddedToWeakSet is just about as good as it gets (although, I'd consider this ternary optimization and renaming it to isObject).
Update: You may also want to consider the logical optimization mentioned in the comments (high-five #Patrick Roberts!).
When should you not sanity check before inserting into [any collection]?
If you have a strict real-time performance requirement, you may want to consider avoiding this check on every insert. If this is your case, here are your options:
1) Check on every insert
Pros: Protects the caller form handling the erroneous value
Cons: Performance cost of doing the check and branch prediction
2) Try / Catch
I would be concerned about setting up the overhead of exception handling with a function that needs high performance...
Pros: Protects the caller from handling the erroneous value
Cons: High performance cost of try/catch overhead (worse than if statement)
3) Let the caller handle the erroneous value
This tactic is about reducing the responsibility of the performance critical function. For some callers, this check may be totally unnecessary. For instance, in one domain, I may know for a fact that I will only ever have objects or nulls. In that case calling add(myWeakset, value || {}) is my best performance option.
Nevertheless, some domains may need the ability to addSafely, so it might be beneficial to expose both methods...
Pros: Caller gets to maximize performance for their domain
Cons: Risk of irresponsible caller and increase in complexity
In JavaScript code I want to replace the double-equals structure of the following if-statement:
if( name == null ) {
//do stuff
}
The double equals fail for the jshint rule "eqeqeq", where it's recommended to replace double equals with triple equals. For a moment, let's imagine the above code changed from == null to === null like this:
if( name === null ) {
//do stuff
}
This would work for a variable explicitly defined having the value null, but unfortunately would fail for any unset variables like this.
var a = null; // works correctly
var b; // will fail in comparison
Previously when the triple-equals rule was important to me I would do the following
if( name === null ||| typeof(name) === 'undefined' )
but I find this extremely bloated.
The best alternative I can come up with now is to use the nature of the if-statement and let it evaluate to a false-ish expression like here where I negate the expression and simply remove the == null part:
if( !name ) {
//do stuff
}
For me, this is much simpler, easy to read, and completely avoids explicit equals comparison. However, I am uncertain if there are any edge causes I am missing out here?
So the question is, can I generally replace == null with the negated expression if statements? If so, what are the pitfalls and exceptions where it wouldn't work? Does it work for general array items, strings, object properties?
My criteria for picking a solution will be
clean code
easy to read and quickly understand
validates jshint rules
works in modern browsers (as of writing January 2015)
I am aware of other slightly related questions for discussing difference in the equality operators == vs ===, but this is merely for a discussion of the evaluation compared to null-ish inside the if-statement.
So the question is, can I generally replace == null with the negated expression if statements?
Probably not universally, no, but perhaps in some places.
If so, what are the pitfalls and exceptions where it wouldn't work? Does it work for general array items, strings, object properties?
The !value check will be true for all of the falsey values, not just null and undefined. The full list is: null, undefined, 0, "", NaN, and of course, false.
So if you have name = "" then
if (!name) {
// ...
}
...will evaluate true and go into the block, where your previous
if (name == null) {
// ...
}
...would not. So just doing it everywhere is likely to introduce problems.
But for situations where you know that you do want to branch on any falsey value, the !value thing is very handy. For instance, if a variable is meant to be undefined (or null) or an object reference, I'll use if (!obj) to test that, because any falsey value is good enough for me there.
If you want to keep using JSHint's === rule, you could give yourself a utility function:
function isNullish(value) {
return value === null || typeof value === "undefined";
}
The overhead of a function call is nothing to be remotely worried about (more), and any decent JavaScript engine will inline it anyway if it's in a hotspot.
I believe jQuery uses undefined in the module pattern to avoid it being redefined to an unexpected value. I thought about doing this but whenever I compare undefined I tend (always?) to use
typeof foo === 'undefined'
So is there any point doing this:
(function (win, doc, RG, undefined)
{
// App goes here
})(window, document, typeof RGraph === 'object' ? RGraph : {});
whenever I compare undefined I tend (always?) to use typeof foo === 'undefined'
Well if you don't use undefined in your module then there is no point in declaring it as a parameter to ensure its value.
However, there's a question mark after your "always", and you can hardly know who else might work with your code in the future, so it still might be advisable.
There are three ways to test whether a value is undefined:
Method 1: Use typeof:
if (typeof value === "undefined") {
// do something
}
Method 2: Compare it with the variable undefined:
if (value === undefined) {
// do something
}
Method 3: Compare it with void 0:
if (value === void 0) {
// do something
}
Q: Which one should you use?
A: There are many factors to consider:
In terms of understandability typeof and undefined are the best.
In terms of minimum characters undefined is the best when minified.
In terms of performance (http://jsperf.com/type-of-undefined-vs-undefined):
Both undefined and void are at par in most browsers (notably Firefox and Chrome).
In Chrome typeof is slower than the other two but in Firefox it is the fastest.
Using typeof is the only way to test whether or not a variable exists.
Based on these factors I would say that undefined (method 2) is the best method. There's always the problem of undefined being overwritten. However if you're using the module pattern (which you should be doing in the browser) then you can get undefined for free:
(function ($, undefined) {
// you can use `undefined` safely here
}(jQuery));
Hence if you're using the module pattern then I would suggest you use the second method. It is readable, understandable and concise. The only disadvantage is that you won't be able to test whether or not a variable exists. However you can always fall back to typeof for that.
Context:
I'm trying to make sure that an object passed to a function is valid before I use it. And I'd like to know if there is any difference between the two approaches, one is more syntactically verbose than the other. If the functionality and behaviors are the same across all JavaScript engines used by modern user agents such Chrome, IE, FF etc., I'd rather use the simpler version. If not, I'd like to use the safe bet so my code behaves as I intended regardless of the JavaScript engine's implementation.
Question:
Given the following function, there are two statements as denoted by comment section.
Does statement 1 yield the same result as statement 2 regardless of the JS engine (or user agents)? And why or why not?
function f(obj) {
if(obj) { // statement 1
console.log('obj is valid');
}
if(obj !== null && obj!==undefined) { // statement 2
console.log('obj is invalid');
}
}
No, they are different.
if (obj) tests for truthy values. In JavaScript everything is truthy except for "", 0, false, NaN, null and undefined (there may be a few more I'm forgetting...)
But what if one of those falsy value is a value you are ok with, such as zero or false? A quantity of zero is a perfectly legit value, as is false for isTotallyLame. So you need a test that allows those values to pass. You need to test for existence.
if (obj!==null && obj!==undefined) tests for existence. Only null or undefined will fail the test. This means that empty string "", zero 0, or even false are valid values because they are a set value.
However, as #Tim notes, the preferred standard test for existence looks like this:
if (typeof obj !== "undefined" && obj !== null)