I'm using JavaScript/jQuery to fetch data as json via an ajax call. The json is then parsed to a JavaScript object. Let's call the object var.
Now I want to assign a value from the object to a variable:
let publicationDay = val["work-summary"][0]["publication-date"]["day"]["value"]
This works, as long this value exists. However, for some results, the object contains null not for ["value"] but for ["day"] (as shown below)
val["work-summary"][0]["publication-date"]["day"] // is null
In this case, let publicationDay = val["work-summary"][0]["publication-date"]["day"]["value"] will throw the following error:
Uncaught TypeError: val['work-summary'][0]['publication-date'].day is null
and the code execution is stopped. Sure, I can check whether val["work-summary"][0]["publication-date"]["day"] === null, but what would be the most elegant way to handle this case? Maybe, in the future even
val['work-summary'][0]['publication-date'] // is null
may occur and my check for null does not work.
I would recommend trying optional chaining.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
Assuming that any of those values could be null:
let publicationDay = val?.["work-summary"]?.[0]?.["publication-date"]?.["day"]?.["value"]
It will set the variable to either day.value or undefined
Depends heavily on your context but here’s some examples.
const publicationDate = val['work-summary'][0]['publication-date'];
// usage
console.log(publicationDate && publicationDate.day && publicationDate.day.value); // very basic
console.log(publicationDate?.day?.value); // optional chaining, better if supported by your environment (should be)
console.log(get(publicationDate, “day.value”)); // lodash get()
Related
I found a really great use case for optional chaining in my react project. I have used it slightly before but this line of code has made me a bit confused.
I have a table that is sortable.
One of the columns shows success or failure depending on if failed_date is null or a date.
sortedRows = sortedRows.sort((a, b) => a?.failed_date?.localeCompare(b?.failed_date));
But What confuses me is which value does the '?.' check is nullish?
Does a.failed_date?.localeCompare() check if failed_date?. is null/undefined or does it check if ?.localeCompare() is null/undefined?
Same with b?.failed_date is it checking b to be nullish? or failed_date.
I think My confusion comes from looking at the Documentation
Because arr?.[50] checks if element 50 is nullish but obj.method?.() checks if the method is not nullish?
Looking at this second block of code (which I believe is now correct) a.failed_date may be nullish and won't do localeCompare if a.failed_date is null?
But what if a.failed_date is not null but b.failed_date is null?
Does localeCompare not care? I haven't gotten an error but I was using localeComapre(b?.failed_date)
sortedRows = sortedRows.sort((a, b) => a.failed_date?.localeCompare(b.failed_date));
Let's say you define variable like below
const variable = { value: 'test' };
then you want to access variable?.value it equals variable === null || variable === undefined ? undefined : variable.value.
Same with array.
Check typescript playground and see js output https://www.typescriptlang.org/play?#code/MYewdgzgLgBAhgJwXAnjAvDA2gXQNwBQBiyKA-AHRYCsADDkA
Basically, the ? in that context means optional chaining.
How it works is, for example, if you define an object like below, and you want to try and access the views property, it will throw an error.
const obj = { website: "stackoverflow.com", };
console.log(obj.views.toFixed(0)); // Error!
This is because you are trying to access a method off of undefined, which doesn't have anything.
To avoid this error, we can use optional chaining, like below.
const obj = { website: "stackoverflow.com", };
console.log(obj?.views?.toFixed(0)); // undefined
Optional chaining tells JavaScript to complete the method/read the key, but if it doesn't exist, instead of throwing an error, just return undefined.
It also works with arrays; the same way! If, say, index 78 doesn't exist, instead of throwing an error, it will just return undefined.
const arr = [1, 2, 3];
console.log(arr?.[78]?.toString());
To elaborate here, it is possible to stack multiple optional chaining operators as seen in OP's code a?.failed_date?.localeCompare(b?.failed_date)
In these cases, it is not a question of which object key value will be checked. The code will be evaluated from left to right and if any of the object values are nullish then undefined will be returned.
Refer to the documentation for further understanding
MDN Optional Chaining
So, I have two ajax calls, which are chained in a promise.
For example
promiseData.then(res1, res2) {
let responseOneParsed = JSON.parse(res1)
let responseTwoParsed = JSON.parse(res2)
}
I am concatenating these two, like this
concatenatedArr = responseOneParsed.data.concat(responseTwoParse.data)
So, this is clear.
But, sometimes one of these two ajax calls returns undefined, since the response is empty (should be), and I get the error:
error TypeError: Cannot read property 'concat' of undefined
Which is again very clear, but how can I scale this code to accept one of these parameters, if other is undefined, so it does not fail? Is .concat() used wrongly here?
You can easily make it with || operator and empty array: []
like this
concatenatedArr = (responseOneParsed.data || []).concat(responseTwoParse.data || [])
Isn't this just a case of proper sanity checking?
Check to see if responseOneParsed.data is valid, if it is, call concat, else, apply the second data.
concatenatedArr = responseOneParsed.data ?
responseOneParsed.data.concat(responseTwoParse.data ? responseTwoParse.data: [] )
:responseTwoParse.data
Suppose I have some variables:
var s = 's', i = 0, o = {}, a = [], n = null, nan = NaN, u;
How can I make any sense of when reading x.p will return undefined, and when it will throw a TypeError?
s.p; // undefined
i.p; // undefined
o.p; // undefined
a.p; // undefined
n.p; // TypeError!
nan.p; // undefined
u.p; // TypeError!
P.S. Are null and undefined the only weird values in this way? Are there others?
Edit
I'm aware that by declaring variables as I have, several of my values have been automatically wrapped by objects (e.g. Number), despite being primitives. Therefore I can treat them like "proper" objects (e.g. {}, []) and attempt to read their properties. But I can't find much explaining what is and isn't wrapped this way.
Yes, null and undefined are the only values that throw an exception when being used in a property access. The dot and bracket property accessors do invoke the internal CheckObjectCoercible function, which is defined as following:
The abstract operation CheckObjectCoercible throws an error if its
argument is a value that cannot be converted to an Object using
ToObject. It is defined by Table 15:
Table 15 — CheckObjectCoercible Results
Argument Type | Result
--------------+------------------
Undefined | Throw a TypeError exception.
Null | Throw a TypeError exception.
Boolean | Return
Number | Return
String | Return
Object | Return
null and undefined being the values that represent Nothing cannot be converted to objects ("wrapped" as you say). Notice that you could of course have an object with an existing property that throws on access, like
var o = {get p() { throw new TypeError("you may not access this"); }};
o.p // TypeError
When you're reading the property of something that's undefined. You're basing saying:
What is undefined of undefined? or in the case of null what is undefined of null ? Neither undefined nor null are objects, therefore you can't set or get properties on/from them. With that in mind, an error will be thrown.
When the object is null/undefined, it'll throw an error because it'll try to access a property of an object that does not exist in first place.
On the other cases, it'll try to access a property of an existing object and will return undefined because the property is not found.
Please note that almost everything in js is an object, as you can see here.
let's firstly start with
why undefined on:
var s='s';
You assigned s to string which is primitive type. Javascript is tricky here in one sense. Though primitive type, when you try to access the property by doing:
s.p
It firstly does this internally:
String(s)
and creates the object and then tries to see if it has that property which is, of course not defined and thus you get undefined. Almost similar things happen with numerical assignment and NAN.
For n=null:
If you do:
var n=null;
typeof n;//"object"
n.p// throws typeerror
In fact,null can be thought of special kind of object without any properties or method to represent absence of value and thus, you get typeerror when you try to access any properties of null.
a.p
o.p
Easily, you can see that you are trying to access the property of an object than is not defined or not built-in and thus it should say, undefined. You should be surprised to see other than that :D.
To add to the other answers here, there is an easy way to check if a property access will throw an exception. Simply loosely compare the value you're unsure of to null, like so:
if(x != null) { // note the LOOSE equality, rather than !==
// do something with x.prop
}
The reason this works is that only null and undefined * throw errors on property access, and they are only loosely equal to each other. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_when_to_use_them.
* There is also a case where some JS objects can apparently "emulate" being undefined, in which case they also are loosely equal to null and undefined.
I have this method
var link = this.find_first_link(selectedElem);
which should return an object. I'm not sure what should it return if no element is found - null, undefined or false? I've rejected 'false' option since I don't think it suits here so I'm choosing betwen null or undefined. I've read that 'undefined' should be used where some kind of exception or error occurs, so currently this method returns null. Is that OK?
Look at what is done in methods you have in your browser.
getElementById returns null when there is no element with the id provided.
That's what null has been designed for : to represent the absence of an object (typeof null is "object"). Use it when the expected returned type is "object" but you have no object to return. It's better than undefined here because you would have undefined before you even decided what to put in the variable or before you even call the function.
From the description of null in the MDN :
In APIs, null is often retrieved in place where an object can be
expected but no object is relevant.
Yes, use null.
Yes,
var link = this.find_first_link(selectedElem);
It will return NULL.
I am trying to access a certain member in a JavaScript object. In order to do this, I need to try out a couple of key values.
For example, Object['text/html'] which will give me an export link for a HTML document. However, not every object of this type will have a text/html key pair value.
In Python I would solve this problem using a Try-Catch block, with the KeyError exception. If I can do something similar in javascript, as in use an exception in a Try-Catch block, that would be great.
However, if alternatives exists instead of try catch blocks, that do achieve the same end goal, I would like to know about them as well.
EDIT:
I would prefer to use an exception over using functions. I do this because the text/html key might not be there, but it should be there. An exception seems more appropriate for this scenario
Javascript doesn't generate an exception when reading or writing a property that doesn't exist. When reading it, it just returns undefined. When writing it, it just creates the property.
You could create your own function that tests to see if the property exists and throws an exception if it does not (but you'd have to call that function whenever), but JS doesn't make an exception out of that on it's own like you are asking for.
If you want to test if a key exists on an object in javascript, you can use this construct with the in operator:
var obj = {};
var key = "test";
if (key in obj) {
// key exists
} else {
// key doesn't exist
}
If you try to read a key that doesn't exist, you will get undefined as the value.
var obj = {};
var value = obj.test;
alert(value === undefined);
The in operator does a better job of telling you whether the key exists that testing for undefined because undefined is a legal value for a key that exists.
In many cases, where you control the values that the keys have and a key that is present will never have a falsey value, you can also just check if the key has a truthy value:
var obj = {};
var obj.test = "hello";
if (obj.test) {
// key exists and has a truthy value
}
If you want to make sure that the object itself has the property and not any prototype that it is inheriting from, then you can do this:
var obj = {};
var obj.test = "hello";
if (obj.hasOwnProperty(test)) {
// key exists on the object itself (not only on the prototype)
}
Read this!
The accepted answer is correct however omits some points.
1) Accessing nested object
Like someone pointed out in the comment, Javascript returns undefined when the key doesn't exists in the object.
However, if you need to access an object inside an object (or an Array, or a function), well this break.
let a = {};
let userName = 'js'
let data = a.response[userName];
Cuz you will received actually a TypeError, basically because we are trying to read a property of undefined, which doesn't have any.
VM187:2 Uncaught TypeError: Cannot read properties of undefined (reading 'js')
at <anonymous>:2:22
2 Answering the question
The Python principle "Ask forgiveness not permission" - explain is actually for the most part working well in Javascript (and PHP, you didn't ask but well..). There are for sure some difference, or some situation where the difference is important, but for most use cases is the same
So this is how you would do it:
try {
let data = a.key1.key2['whatever'].nested.damn.object;
console.log(data)
} catch (error) {
let data = "noope";
console.log(data);
}
As you can see, in Javascript you don't really care about the error type, (for the most part, sure other situation you should case). Is almost like anything is in a Python's
try:
a = "hello" + 1 + {} + [] # crazy stuff here
except BaseException as bleh:
print(str(bleh))
Documentatin
MDN Working with objects
How do I check if an object has a key in JavaScript? [duplicate]