I have a client that makes rest web service call and from calling the service returned data is JSON object. It has following field names as name and age. I ran into a case where fields are empty in returned data. Received this {} instead of {"name":"john doe","age":"23"}. What would be the best way to handle exception like that in JavaScript?
It depends on the data you're expecting.
The classic way would be
if (typeof object.property === "undefined") {
...
}
which also implies that undefined is not an acceptable value (and it shouldn't). But if it's not good for you, there's always
if (!object.hasOwnProperty("property")) {
...
}
If you expect a non-empty string (or a non-zero number), you can shortcut to:
if (!object.property) {
...
}
But if you need to accept empty strings or zeroes, this is very common too:
if (object.property == null) {
...
}
This rejects values like undefined (which should be your case) and null (which may be another unacceptable value).
But, in the end, instead of an empty object I'd recommend to return null and do like this:
if (object != null) {
// do your stuff with name and age
...
}
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.
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 "".
I would like to display some content only if an object's child array has elements in it. But sometimes the object itself is not defined so doing this fails if object or object.child is not present at the scope:
if(object.child.length){
alert('hello world');
}
Which results in:
Uncaught ReferenceError: object is not defined
So I have to add two additional if conditions to check if the object and it's child is defined or not:
if(typeof object !== 'undefined'){
if(typeof object.child !== 'undefined'){
if(object.child.length){
alert('hello world');
}
}
}
Writing a function around this is problematic too:
function isset(value){ ... }
if(isset(object.child.length)({ ... } // <-- still raises error that object is not defined
Is there a cleaner, shorter way to do this?
You can put a guard on:
if(object && object.child && object.child.length){
The above defends against object and object.child being undefined or null (or any other falsey value); it works because all non-null object references are truthy, so you can avoid the verbose typeof object !== "undefined" form. You may not need both of the guards above, if you know for sure that object.child will exist if object does. But having both is harmless.
It's worth noting that this is useful even when retrieving values, not just for testing them. For instance, suppose you have (or may not have!) object.foo, which contains a value you want to use.
var f = object && object.foo;
If object is falsey (undefined or null are the typical cases), then f will receive that falsey value (undefined or null). If object is truthy, f will receive the value of object.foo.
|| is curiously powerful in a similar way.
Update: I have created a ticket: http://bugs.jquery.com/ticket/12191
jQuery's $.type() function returns the [[Class]] internal property (lower-cased) of an object. E.g.:
$.type( {} ) // "object"
$.type( [] ) // "array"
$.type( function () {} ) // "function"
However, it only works for these types of objects:
Boolean Number String Function Array Date RegExp Object
specified by this section of jQuery's source code:
// Populate the class2type map
jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
class2type[ "[object " + name + "]" ] = name.toLowerCase();
});
In addition to those types of objects, the ECMAScript standard defines corresponding [[Class]] internal properties for these:
Arguments Error JSON Math
This is specified in this sentence of the ECMAScript standard (in section 8.6.2):
The value of the [[Class]] internal property of a host object may be
any String value except one of "Arguments", "Array", "Boolean",
"Date", "Error", "Function", "JSON", "Math", "Number", "Object",
"RegExp", and "String".
$.type returns "object" for those types of objects:
$.type( new Error ) // "object"
$.type( JSON ) // "object"
$.type( Math ) // "object"
(function () { $.type( arguments ); /* "object" */ }())
instead of "error", "json", "math", and "arguments", which are the actual [[Class]] values here (capitalized).
I would like to make it clear that $.type could return those correct values if it wanted to, since it uses the Object.prototype.toString.call() retrieval method, which returns "[object Error]" for Error objects, for instance.
So, why does jQuery report "object" instead of those four values? I could understand JSON and Math, since those are not instances, but singleton objects. And I could even understand arguments, since that is an automatically provided object, instead of an instance explicitly created by a JavaScript program (as in var args = new Arguments;). Buy why errors? I don't see what makes Error objects special (compared to the other native types, like Date, Array, etc.).
tl;dr
$.type( new Error ) // why does this return "object" instead of "error"?
Update: Just to clarify one thing: I know why $.type returns "object" for Error instances, as I have looked into its source code, and found the code that is responsible for this behavior. I would like to know why $.type is defined to behave in such a manner.
Its because jQuery's authors either forgot about that type or they didn't care about it.
Since
Object.prototype.toString.call( new Error );
will correctly return [object Error].
So, if you're cool to have a slightly longer comparison strings, here we go:
(function( type ) {
console.log( type(new Error) );
}( Function.prototype.call.bind( Object.prototype.toString )));
Beside that, there are other "types" which jQuery doesn't care about like JSON or any DOMElement. For instance, $.type( JSON ) also returns object while the above type methods correctly returns [object JSON].
Another example $.type( document.body ) again returns object where my simple type() methods again correctly returns [object HTMLBodyElement].
So long story short, jQuery does not abstract all [[Class]] values from all available types. There might be a reason for beeing browser compatible, but for the Error type, there is none.
Actually there are a zillion types, alone for all the DOM objects. I can imagine that the jQuery authors only wanted to support native object types and no host object types which essentially JSON and any DOMxxxElement etc. are. I'm not sure about the Error object whether its native or host tho.
OK, so I submitted a ticket here: http://bugs.jquery.com/ticket/12191. It has been classified as a bug, and it is expected to be fixed in version 1.8.1.
I reckon the Error type was considered unreliable, a lot of code throws things, that needn't be returned by the Error constructor:
function foo()
{
throw 'I\'m what a lot of people call an error';
}
try
{
foo();
}
catch(e)
{
console.log(Object.prototype.toString.call(e));//[object String]
}
The omission of Arguments is even simpler to explain: 'use strict'; rather limits what you can do with the arguments object. I think it better not to promote the usage of it too much, and so does John, I think. (sorry, never spell his last name correctly - I'm not on 1st name basis with the man, though).
Math is, by many considered a bad idea for an object, it should've been just a set of functions (according to most JS people). Anyway, when would anyone pass the 1 Math instance to the $.type method? you've just Typed the Constructor! what purpose could it possibly serve to have that repeated to you?
JSON is an object, older versions of IE don't support it, so you have to include a file (like you have to do with jQuery) to have the JSON object. It's not in any way a native type, but well written format sprouted from DC's mind. What else can JS say about it, other then the fact that it's an object?
I am trying to pass a simple string to an event listener in order to identify an appropriate array object to modify. I understand by looking at the log (shown in comments) that what I am passing is an object and it can't be compared directly to another object's property.
Should I pass an object that has the string as a property (like the array object does), or is there a more appropriate way to reference the object as a string or call its name somehow (like the log does in the first comment)?
// I just want to pass a string as an argument. Here is a static example.
var timestampID = '1307740835264';
Ti.App.fireEvent('confirmation',timestampID);
Notice how the first log output interprets the argument as a string, but the if comparison recognizes it as an object.
Ti.App.addEventListener('confirmation',function(timestampID) { // produces "[DEBUG] fire app event: confirmation with 1307740835264"
Ti.API.info(timestampID); // produces "[INFO] { type = confirmation; }"
for (i=0;i<myArray.length;i++) {
Ti.API.info(myArray[i].timestampID + ' | ' + timestampID); // produces "[INFO] 1307740835264 | [object Object]"
if (myArray[i].timestampID == timestampID) { // will produce false
// will never happen
}
}
});
Thanks!
So to me it looks like the timestampID being passed in to the handler is an object, however from the second statement (and accompanying [INFO] comment), I have absolutely no idea what properties it has. Let's assume that it has a property called timestamp. Then your if statement should be
if (myArray[i].timestampID === timestampID.timestamp)
But that's about all I can glean from this code snippet I'm afraid.
UPDATE: OK, I see how you're triggering the event. The Titanium API is bafflingly obtuse on this point (it looks like the help for fireEvent is wrong: two parameters called "name"?). There are no examples that I can see. However it does say that what you pass in as the data is (must be?) serialized as JSON.
Now that I know that, looking at the second statement's [INFO] line it makes more sense. It's a string that has the JSON data. Your data is missing since it was a string value. Try this to fire:
Ti.App.fireEvent('confirmation', { timestampID: '1307740835264' } );
and this in the event handler:
Ti.App.addEventListener('confirmation', function (data) {
for (i=0;i<myArray.length;i++) {
if (myArray[i].timestampID === data.timestampID) {
...code...
}
}
});
Either write a comparison function or find the scalar property (Number or String) that you want to compare.