36

In Rails I can do this:

 x = user.try(:name)

this method returns nil if user is nil else user.name. Here name is a method defined on the user object.

I know it can be done using if..then..else in Javascript but is there an equivalent compact method to do the same in Javascript?

Googling points to Javascript's try command which is not what I am looking for.

tihom
  • 7,923
  • 1
  • 25
  • 29
  • Checkout this question, lots of good answers: http://stackoverflow.com/questions/6613952/is-there-a-null-coalescing-elvis-operator-or-safe-navigation-operator-in-javas – Abdullah Jibaly Oct 10 '13 at 01:40
  • @AbdullahJibaly it's not the same thing, the Elvis operator would be doing `user.name || "..."` for instance. But user could be `nil / undefined` so this could throw an error. Here `user.try(:name) / user&.name` is equivalent to `(user || {}).name` as pointed out in the answers – Dorian Jan 22 '17 at 05:10
  • @AbdullahJibaly to be fair, it mentions the Safe Navigation Operator / Existential Operator `?.` (or `&.` in ruby) that CoffeeScript implements – Dorian Jan 22 '17 at 05:18
  • https://en.wikipedia.org/wiki/Safe_navigation_operator – Dorian Jan 22 '17 at 05:27
  • This link should also be relevant here: https://github.com/tc39/proposal-optional-chaining – 3limin4t0r Aug 25 '18 at 10:24

6 Answers6

49

You can use optional chaining

Examples:

// Access Properties

user?.name; // user might be null/undefined

user.name?.firstName // user will be available however name is not guaranteed.

// Access array values

addresses?.[index]; // array addresses might be undefined

// May be function??

user.getGeolocation?.(); // If the function exists execute it.

Slightly unrelated but something around handling of null/undefined is another feature called Nullish coalescing operator ??

// Example:


function foo(input) // some Array as input {
   //.. Some stuff
   return [input??[]].concat(bar); //if input is empty ignore and concat bar on an empty array and return.
}

//----

const defaultVal = 'someVal';
...
const val = this.someObj.prop1??defaultVal;

Below is the outdated solution prior to Optional chaining became native to Javascript:


You can do this way, as there is no built in way of doing that:

var x = (user || {}).name;
  • If user is not defined/null you will get undefined
  • If user is defined you will get the name property (which may be set or undefined).

This won't break the script if user is not defined (null).

But user variable has to be declared some where in the scope, even though its value is not defined. Otherwise you will get the err saying user is not defined.

Similarly if is in global scope then you can explicitly check for this variable as a property of global scope, to avoid the error as mentioned above

ex:

 var x = (window.user || {}).name; // or var x = (global.user || {}).name;

For safe execution of functions,

 var noop = function(){}; //Just a no operation function

 (windowOrSomeObj.exec || noop)(); //Even if there is no property with the name `exec` exists in the object, it will still not fail and you can avoid a check. However this is just a truthy check so you may want to use it only if you are sure the property if exists on the object will be a function.
PSL
  • 123,204
  • 21
  • 253
  • 243
12

Optional Chaining Operator

Is a new proposal for ECMAScript.

It is in an early stage but we can start using it with babel.

This is the problem:

const person = {name: 'santiago'}
let zip = person.address.zip // Cannot read property 'zip' of undefined

This is how it works:

const person = {name: 'santiago'}
let zip = person?.address?.zip // undefined

To start using it we need babel alpha 7:

npm install --save-dev babel-cli@7.0.0-alpha.19
npm install --save-dev babel-plugin-transform-optional-chaining@^7.0.0-alpha.13.1

And we need to add the plugin to our .babelrc

{
  "plugins": ["transform-optional-chaining"]
}

Adam Bene Medium Post which explains how to use it and another use cases

SiM
  • 186
  • 1
  • 7
8

You can use logical AND (&&) operator. It works differently than in most of the languages because its result is not a boolean. For example:

const x = user && user.name;

The table below shows all possible outcomes of using this operator:

+--------------+-----------------------+
|    user      | x = user && user.name |
+--------------+-----------------------+
| undefined    | undefined             |
| null         | null                  |
| {}           | undefined             |
| {name:''}    | ''                    |
| {name:'Jon'} | 'Jon'                 |
+--------------+-----------------------+
Michał Miszczyszyn
  • 11,835
  • 2
  • 35
  • 53
2

Isn't that the same as what this gives you in Javascript:

user['name']
user.name

This returns undefined instead of nil but that's equivalent in most cases. I think the reason Rails needs the try method is because you cannot access instance variables unless they are made accessible.

To add to @PSL's approach, I usually do something like:

var x = user || {},
    y = x.name || {},
    z = y.first_name;
Abdullah Jibaly
  • 53,220
  • 42
  • 124
  • 197
  • I am looking to call methods on the object e.g. `str.match(/\d/)` – tihom Oct 10 '13 at 01:28
  • I believe Rails uses try because everything is a method. user['name'] will return an accessor 'name', but won't return the method 'name' on the user instance. – zungaphobia Oct 10 '13 at 01:33
2

In Rails4, try does this:

Invokes the public method whose name goes as first argument just like public_send does, except that if the receiver does not respond to it the call returns nil rather than raising an exception.

In Rails3, try is like try! in Rails4 and that's the same as try except that it complains if the object doesn't understand the method you're trying to call. The original intent of try was to hide a bunch of nil checks so that you could say:

o.try(:m)

instead of

o.nil?? nil : o.m

The Rails4 version also hides a check if the object understands the method you want to call.

There's no special syntax for this in JavaScript but you can sweep the ugliness into a function.

The Rails4 version of try would look like this function in JavaScript:

function rtry(obj, m) {
    if(obj == null)
        return null;
    if(typeof obj[m] === 'function')
        return obj[m].apply(obj, [].slice.call(arguments, 2));
    return null;
}

and you'd say things like x = rtry(obj, 'method_name', arg1, arg2, ...) and x would be null if obj didn't understand method_name (including if obj is null or undefined).

Demo: http://jsfiddle.net/ambiguous/BjgjL/

A Rails3 version is simple, that's just a null check:

function rtry(obj, m) {
    if(obj == null)
        return null;
    return obj[m].apply(obj, [].slice.call(arguments, 2));
}

and JavaScript itself will raise an exception of obj doesn't have an m method. This version is equivalent to the "is it a function" version of CoffeeScript's ? existential operator.

Demo: http://jsfiddle.net/ambiguous/FQCS2/


As usual, things might not work out so well if you're dealing with native methods rather than methods that you've written in JavaScript. typeof (6).toString might be 'function' in one JavaScript environment but I don't think it is guaranteed to always be 'function' everywhere. The primary use (i.e. hide null and undefined checks) should work everywhere though.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
0

I don't think so. Why not roll your own? Not exactly equivalent but does the job.

function tryMe(obj) {
  return obj === null || obj === undefined ? null : obj;
}
zungaphobia
  • 370
  • 2
  • 7