272

I'll explain by example:

Elvis Operator (?: )

The "Elvis operator" is a shortening of Java's ternary operator. One instance of where this is handy is for returning a 'sensible default' value if an expression resolves to false or null. A simple example might look like this:

def gender = user.male ? "male" : "female"  //traditional ternary operator usage

def displayName = user.name ?: "Anonymous"  //more compact Elvis operator

Safe Navigation Operator (?.)

The Safe Navigation operator is used to avoid a NullPointerException. Typically when you have a reference to an object you might need to verify that it is not null before accessing methods or properties of the object. To avoid this, the safe navigation operator will simply return null instead of throwing an exception, like so:

def user = User.find( "admin" )           //this might be null if 'admin' does not exist
def streetName = user?.address?.street    //streetName will be null if user or user.address is null - no NPE thrown
user3840170
  • 26,597
  • 4
  • 30
  • 62
tiagomac
  • 2,935
  • 2
  • 15
  • 6
  • 10
    The 'Elvis Operator' exists in C# -- but it's called the null coalescing operator (much less exciting) :-) – Cameron Jul 07 '11 at 16:34
  • If you want an alternative syntax you can take a look of cofeescript – Lime Jul 07 '11 at 16:40
  • 5
    This question is sort of a mess... it is mixing up 3 different operators ? : (ternery operator, spelled out in the question, possibly a typo), ?? (null coalescing, which does exist in JavaScript) and ?. (Elvis) which does NOT exist in JavaScript. The answers do not clarify this distinction very well. – JoelFan Mar 10 '15 at 01:40
  • 3
    @JoelFan can you provide a link to documentation regarding proper null-coalescence (`??`) in javascript? Everything I'm finding so far suggests that JS only has "falsey" coalescing (using `||`). – Charles Wood Jun 14 '16 at 15:49
  • @CharlesWood... You are 100% correct... you can only use || as a null coalescing operator if you are sure that none of your expressions are going to return 0 or false (so practically, that none of them are of type Number or Boolean). There are plenty of such cases though... for example, you may know that everything is going to be either an Object or String... but you are correct that it's trickier in JS than C# – JoelFan Jun 16 '16 at 20:47
  • @JoelFan Thanks for the followup. I guess it was a typo when you said that JS has `??` :D – Charles Wood Jun 16 '16 at 20:49
  • 1
    Well, I didn't mean to say that JS literally had ?? but that it had null-coalesce... but even there I was kind of wrong. That being said, I have seen a LOT of JS code that uses || as a null coalesce, despite the falsey pitfalls – JoelFan Jun 16 '16 at 20:50
  • At the point of this comment this is a Conditional (ternary) Operator. I am amused and confused by Elvis mention but it seems relevant given code sample in the docs referenced [1], `var elvisLives = Math.PI > 4 ? "Yep" : "Nope";`. [1]:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator – Gibron Sep 27 '16 at 18:13
  • Safe navigation is a Stage-2 (although still listed as stage-1) proposal. https://github.com/tc39/proposal-optional-chaining – Shammoo Dec 06 '18 at 16:48
  • Does this answer your question? [Is there a "null coalescing" operator in JavaScript?](https://stackoverflow.com/questions/476436/is-there-a-null-coalescing-operator-in-javascript) – Michael Freidgeim Jun 05 '21 at 01:43
  • For those looking from now on, JS now has both operators. *NullCoalesce* is `??` not `?:`, *Optional Chaining* is `?.`. – user1944491 Aug 29 '22 at 11:53

22 Answers22

171

You can use the logical 'OR' operator in place of the Elvis operator:

For example displayname = user.name || "Anonymous" .

But Javascript currently doesn't have the other functionality. I'd recommend looking at CoffeeScript if you want an alternative syntax. It has some shorthand that is similar to what you are looking for.

For example The Existential Operator

zip = lottery.drawWinner?().address?.zipcode

Function shortcuts

()->  // equivalent to function(){}

Sexy function calling

func 'arg1','arg2' // equivalent to func('arg1','arg2')

There is also multiline comments and classes. Obviously you have to compile this to javascript or insert into the page as <script type='text/coffeescript>' but it adds a lot of functionality :) . Using <script type='text/coffeescript'> is really only intended for development and not production.

andrewf
  • 1,350
  • 1
  • 13
  • 19
Lime
  • 13,400
  • 11
  • 56
  • 88
  • 19
    logical or is not quite the thing needed in most cases, as you may want it to pick the right operand only if the left is undefined but not when it is defined and falsy. – user2451227 Aug 07 '14 at 09:06
  • Is it my mistake, or it is really ` – JCCM Jul 07 '15 at 01:12
  • 2
    The CoffeeScript home page uses ` – Elias Zamaria Aug 09 '16 at 22:09
  • 28
    While this answers the question it is almost entirely about coffeescript rather than javascript, and is more than half about describing coffeescript benefits unrelated to the OP. I'd suggest boiling it down to just what's relevant to the question, as wonderful as coffeescript's other benefits are. – jinglesthula Oct 04 '16 at 17:25
  • 4
    Am I going bananas? Surely the objection of user2451227 (currently with 4 votes) is invalid because the ternary's middle operand (i.e. right operand with the Elvis operator) would equally not be picked if the expression/left operand is defined and falsy. In both cases you then have to go `x === undefined`. – mike rodent Jan 20 '18 at 17:56
  • @mikerodent, My thoughts exactly. – Neowizard Apr 12 '18 at 08:58
  • 5
    Please consider updating this to mention the [optional chaining operator, `?.`,](//developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining). [Browser support](//developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining#Browser_compatibility) isn't at the point where I'd use it for general code, but it's heading in that direction. In addition, there's now the [nullish coalescing operator (??)](//developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_Coalescing_Operator#Browser_compatibility), with a similar status. – Makyen Aug 02 '20 at 16:13
  • lmao, all these ways to avoid the correct solution of implementing the Option monad correctly. – Henry Henrinson Nov 18 '20 at 14:33
142

I think the following is equivalent to the safe navigation operator, although a bit longer:

var streetName = user && user.address && user.address.street;

streetName will then be either the value of user.address.street or undefined.

If you want it to default to something else you can combine with the above shortcut or to give:

var streetName = (user && user.address && user.address.street) || "Unknown Street";
samjudson
  • 56,243
  • 7
  • 59
  • 69
140

2020 Update

JavaScript now has equivalents for both the Elvis Operator and the Safe Navigation Operator.


Safe Property Access

The optional chaining operator (?.) is currently a stage 4 ECMAScript proposal. You can use it today with Babel.

// `undefined` if either `a` or `b` are `null`/`undefined`. `a.b.c` otherwise.
const myVariable = a?.b?.c;

The logical AND operator (&&) is the "old", more-verbose way to handle this scenario.

const myVariable = a && a.b && a.b.c;

Providing a Default

The nullish coalescing operator (??) is currently a stage 4 ECMAScript proposal. You can use it today with Babel. It allows you to set a default value if the left-hand side of the operator is a nullary value (null/undefined).

const myVariable = a?.b?.c ?? 'Some other value';

// Evaluates to 'Some other value'
const myVariable2 = null ?? 'Some other value';

// Evaluates to ''
const myVariable3 = '' ?? 'Some other value';

The logical OR operator (||) is an alternative solution with slightly different behavior. It allows you to set a default value if the left-hand side of the operator is falsy. Note that the result of myVariable3 below differs from myVariable3 above.

const myVariable = a?.b?.c || 'Some other value';

// Evaluates to 'Some other value'
const myVariable2 = null || 'Some other value';

// Evaluates to 'Some other value'
const myVariable3 = '' || 'Some other value';
jabacchetta
  • 45,013
  • 9
  • 63
  • 75
  • 7
    This answer needs more upvotes. Nullish Coalescing Operator is now in stage 4. – Yerke Jun 08 '20 at 08:15
  • 3
    `a && a.b && a.c` should be `a && a.b && a.b.c`. I can't edit that in myself because it's not a big enough change for SO to accept and I don't want to do the "change inconsequential things to make it to 6 characters" thing. – Emily Aug 20 '20 at 03:43
  • You could add the way to do this with the [] syntax - from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining , all of these are possible: obj.val?.prop obj.val?.[expr] obj.arr?.[index] obj.func?.(args) – Max Waterman Nov 19 '20 at 07:01
  • String I tried this in the developer tools and one would have expected the result for "const result = a?.b?.c ?? 'Some other value';" to be "Some other value" since I haven't defined "a" but it just throws an "Uncaught ReferenceError: a is not defined". "Is not defined" = undefined right? – Jacques Jul 20 '22 at 13:38
89

Javascript's logical OR operator is short-circuiting and can replace your "Elvis" operator:

var displayName = user.name || "Anonymous";

However, to my knowledge there's no equivalent to your ?. operator.

Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
  • 17
    +1, I forgot `||` could be used that way. Be aware that this will coalesce not only when the expression is `null`, but also when it's undefined, `false`, `0`, or the empty string. – Cameron Jul 07 '11 at 16:41
  • @Cameron, indeed, but that's mentioned in the question and seems to be the questioner's intent. `""` or `0` might be unexpected, though :) – Frédéric Hamidi Jul 07 '11 at 16:42
86

I've occasionally found the following idiom useful:

a?.b?.c

can be rewritten as:

((a||{}).b||{}).c

This takes advantage of the fact that getting unknown attributes on an object returns undefined, rather than throwing an exception as it does on null or undefined, so we replace null and undefined with an empty object before navigating.

James_pic
  • 3,240
  • 19
  • 24
  • 15
    Well, it's hard to read but it's better than that verbose `&&` method. +1. – shriek Sep 15 '16 at 21:06
  • 1
    That's the only real safe operator in javascript actually. The logical 'OR' operator that is mentioned above is something else. – vasilakisfil Jun 21 '17 at 13:55
  • @Filippos can you give an example of different behavior in logical OR vs && method ? I can't think of a difference – Nate Anderson Jul 04 '17 at 18:37
  • It also allows navigating an anonymous value without assigning it to a variable first. – Matt Jenkins Dec 01 '17 at 13:59
  • 1
    Love it! Really useful if you want to get the property of an object after an array.find() operation that might not return any results – Shiraz Apr 30 '19 at 14:34
25

i think lodash _.get() can help here, as in _.get(user, 'name'), and more complex tasks like _.get(o, 'a[0].b.c', 'default-value')

tony_k
  • 1,983
  • 2
  • 20
  • 27
  • 8
    My main issue with this method is the fact that since the name of the properties are string, you can't use refactoring functionalities of your IDE with a 100% trust anymore – RPDeshaies Oct 15 '18 at 23:04
23

There is currently a draft spec:

https://github.com/tc39/proposal-optional-chaining

https://tc39.github.io/proposal-optional-chaining/

For now, though, I like to use lodash get(object, path [,defaultValue]) or dlv delve(obj, keypath)

Update (as of Dec 23, 2019):

optional chaining has moved to stage 4

Jack
  • 1,901
  • 1
  • 19
  • 32
13

For the former, you can use ||. The Javascript "logical or" operator, rather than simply returning canned true and false values, follows the rule of returning its left argument if it is true, and otherwise evaluating and returning its right argument. When you're only interested in the truth value it works out the same, but it also means that foo || bar || baz returns the leftmost one of foo, bar, or baz that contains a true value.

You won't find one that can distinguish false from null, though, and 0 and empty string are false values, so avoid using the value || default construct where value can legitimately be 0 or "".

hobbs
  • 223,387
  • 19
  • 210
  • 288
  • 4
    Good job noting that this can result in unexpected behavior when the left operand is a non-null falsey value. – Shog9 Jul 07 '11 at 16:38
12

Yes, there is!

Optional chaining is in stage 4 and this enables you to use the user?.address?.street formula.

If you can't wait the release, install @babel/plugin-proposal-optional-chaining and you can use it. Here are my settings which works for me, or just read Nimmo's article.

// package.json

{
  "name": "optional-chaining-test",
  "version": "1.0.0",
  "main": "index.js",
  "devDependencies": {
    "@babel/plugin-proposal-optional-chaining": "7.2.0",
    "@babel/core": "7.2.0",
    "@babel/preset-env": "^7.5.5"
  }
  ...
}
// .babelrc

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "debug": true
      }
    ]
  ],
  "plugins": [
    "@babel/plugin-proposal-optional-chaining"
  ]
}
// index.js

console.log(user?.address?.street);  // it works
Community
  • 1
  • 1
gazdagergo
  • 6,187
  • 1
  • 31
  • 45
  • 4
    He asked if there was one, not whether you could add one. I think this isnt super useful considering its not what was asked. – DeanMWake Aug 29 '19 at 13:22
  • 2
    It reached stage 3 of the ECMAScript standardization process. es2020 -- https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining – wedi Sep 18 '19 at 08:54
  • I think this answer is misleading as is. – Leonardo Raele Oct 11 '19 at 12:13
  • 1
    This answer isn't quite correct! [Optional chaining](https://github.com/tc39/proposal-optional-chaining) is still in stage 3 and ES2020 hasn't been released or even finalised yet. At least you mentioned how one can use it without having to wait for it to be released. – Maxie Berkmann Nov 28 '19 at 11:41
  • @gazdagergo No problem :). – Maxie Berkmann Nov 29 '19 at 11:12
6

Here's a simple elvis operator equivalent:

function elvis(object, path) {
    return path ? path.split('.').reduce(function (nestedObject, key) {
        return nestedObject && nestedObject[key];
    }, object) : object;
}

> var o = { a: { b: 2 }, c: 3 };
> elvis(o)

{ a: { b: 2 }, c: 3 }

> elvis(o, 'a');

{ b: 2 }

> elvis(o, 'a.b');

2

> elvis(o, 'x');

undefined
cdmckay
  • 31,832
  • 25
  • 83
  • 114
5

UPDATE SEP 2019

Yes, JS now supports this. Optional chaining is coming soon to v8 read more

nullspace
  • 870
  • 10
  • 12
5

You can achieve roughly the same effect by saying:

var displayName = user.name || "Anonymous";
Justin Ethier
  • 131,333
  • 52
  • 229
  • 284
3

This is more commonly known as a null-coalescing operator. Javascript does not have one.

CassOnMars
  • 6,153
  • 2
  • 32
  • 47
  • 3
    true in the strict sense, but as other answers have noted JavaScript's logical OR operator can behave as sort of a *false* -coalescing operator, allowing you to achieve the same brevity in many situations. – Shog9 Jul 07 '11 at 16:40
  • 1
    This is not a null-coalescing operator. Null-coalescing only works on a single value, not on a chain of property access/function invocations. You can already do null-coalescing with the logical OR operator in JavaScript. –  Nov 18 '16 at 21:52
  • No, you can do false-coalescing with the logical OR in JavaScript. – andresp May 15 '17 at 13:45
2

You could roll your own:

function resolve(objectToGetValueFrom, stringOfDotSeparatedParameters) {
    var returnObject = objectToGetValueFrom,
        parameters = stringOfDotSeparatedParameters.split('.'),
        i,
        parameter;

    for (i = 0; i < parameters.length; i++) {
        parameter = parameters[i];

        returnObject = returnObject[parameter];

        if (returnObject === undefined) {
            break;
        }
    }
    return returnObject;
};

And use it like this:

var result = resolve(obj, 'a.b.c.d'); 

* result is undefined if any one of a, b, c or d is undefined.

SU3
  • 5,064
  • 3
  • 35
  • 66
Pylinux
  • 11,278
  • 4
  • 60
  • 67
2

I have a solution for that, tailor it to your own needs, an excerpt from one of my libs:

    elvisStructureSeparator: '.',

    // An Elvis operator replacement. See:
    // http://coffeescript.org/ --> The Existential Operator
    // http://fantom.org/doc/docLang/Expressions.html#safeInvoke
    //
    // The fn parameter has a SPECIAL SYNTAX. E.g.
    // some.structure['with a selector like this'].value transforms to
    // 'some.structure.with a selector like this.value' as an fn parameter.
    //
    // Configurable with tulebox.elvisStructureSeparator.
    //
    // Usage examples: 
    // tulebox.elvis(scope, 'arbitrary.path.to.a.function', fnParamA, fnParamB, fnParamC);
    // tulebox.elvis(this, 'currentNode.favicon.filename');
    elvis: function (scope, fn) {
        tulebox.dbg('tulebox.elvis(' + scope + ', ' + fn + ', args...)');

        var implicitMsg = '....implicit value: undefined ';

        if (arguments.length < 2) {
            tulebox.dbg(implicitMsg + '(1)');
            return undefined;
        }

        // prepare args
        var args = [].slice.call(arguments, 2);
        if (scope === null || fn === null || scope === undefined || fn === undefined 
            || typeof fn !== 'string') {
            tulebox.dbg(implicitMsg + '(2)');
            return undefined;   
        }

        // check levels
        var levels = fn.split(tulebox.elvisStructureSeparator);
        if (levels.length < 1) {
            tulebox.dbg(implicitMsg + '(3)');
            return undefined;
        }

        var lastLevel = scope;

        for (var i = 0; i < levels.length; i++) {
            if (lastLevel[levels[i]] === undefined) {
                tulebox.dbg(implicitMsg + '(4)');
                return undefined;
            }
            lastLevel = lastLevel[levels[i]];
        }

        // real return value
        if (typeof lastLevel === 'function') {
            var ret = lastLevel.apply(scope, args);
            tulebox.dbg('....function value: ' + ret);
            return ret;
        } else {
            tulebox.dbg('....direct value: ' + lastLevel);
            return lastLevel;
        }
    },

works like a charm. Enjoy the less pain!

balazstth
  • 29
  • 1
1

I read this article (https://www.beyondjava.net/elvis-operator-aka-safe-navigation-javascript-typescript) and modified the solution using Proxies.

function safe(obj) {
    return new Proxy(obj, {
        get: function(target, name) {
            const result = target[name];
            if (!!result) {
                return (result instanceof Object)? safe(result) : result;
            }
            return safe.nullObj;
        },
    });
}

safe.nullObj = safe({});
safe.safeGet= function(obj, expression) {
    let safeObj = safe(obj);
    let safeResult = expression(safeObj);

    if (safeResult === safe.nullObj) {
        return undefined;
    }
    return safeResult;
}

You call it like this:

safe.safeGet(example, (x) => x.foo.woo)

The result will be undefined for an expression that encounters null or undefined along its path. You could go wild and modify the Object prototype!

Object.prototype.getSafe = function (expression) {
    return safe.safeGet(this, expression);
};

example.getSafe((x) => x.foo.woo);
Sam
  • 1,725
  • 1
  • 17
  • 28
1

Jumping in very late, there's a proposal[1] for optional chaining currently at stage 2, with a babel plugin[2] available. It's not currently in any browser I am aware of.

  1. https://github.com/tc39/proposal-optional-chaining
  2. https://www.npmjs.com/package/@babel/plugin-proposal-optional-chaining
Tracker1
  • 19,103
  • 12
  • 80
  • 106
1

This was a problem for me for a long time. I had to come up with a solution that can be easily migrated once we get Elvis operator or something.

This is what I use; works for both arrays and objects

put this in tools.js file or something

// this will create the object/array if null
Object.prototype.__ = function (prop) {
    if (this[prop] === undefined)
        this[prop] = typeof prop == 'number' ? [] : {}
    return this[prop]
};

// this will just check if object/array is null
Object.prototype._ = function (prop) {
    return this[prop] === undefined ? {} : this[prop]
};

usage example:

let student = {
    classes: [
        'math',
        'whatev'
    ],
    scores: {
        math: 9,
        whatev: 20
    },
    loans: [
        200,
        { 'hey': 'sup' },
        500,
        300,
        8000,
        3000000
    ]
}

// use one underscore to test

console.log(student._('classes')._(0)) // math
console.log(student._('classes')._(3)) // {}
console.log(student._('sports')._(3)._('injuries')) // {}
console.log(student._('scores')._('whatev')) // 20
console.log(student._('blabla')._('whatev')) // {}
console.log(student._('loans')._(2)) // 500 
console.log(student._('loans')._(1)._('hey')) // sup
console.log(student._('loans')._(6)._('hey')) // {} 

// use two underscores to create if null

student.__('loans').__(6)['test'] = 'whatev'

console.log(student.__('loans').__(6).__('test')) // whatev

well I know it makes the code a bit unreadable but it's a simple one liner solution and works great. I hope it helps someone :)

Neut
  • 59
  • 6
0

This was an interesting solution for the safe navigation operator using some mixin..

http://jsfiddle.net/avernet/npcmv/

  // Assume you have the following data structure
  var companies = {
      orbeon: {
          cfo: "Erik",
          cto: "Alex"
      }
  };

  // Extend Underscore.js
  _.mixin({ 
      // Safe navigation
      attr: function(obj, name) { return obj == null ? obj : obj[name]; },
      // So we can chain console.log
      log: function(obj) { console.log(obj); }
  });

  // Shortcut, 'cause I'm lazy
  var C = _(companies).chain();

  // Simple case: returns Erik
  C.attr("orbeon").attr("cfo").log();
  // Simple case too, no CEO in Orbeon, returns undefined
  C.attr("orbeon").attr("ceo").log();
  // IBM unknown, but doesn't lead to an error, returns undefined
  C.attr("ibm").attr("ceo").log();
Dean Hiller
  • 19,235
  • 25
  • 129
  • 212
0

I created a package that makes this a lot easier to use.

NPM jsdig Github jsdig

You can handle simple things like and object:

const world = {
  locations: {
    europe: 'Munich',
    usa: 'Indianapolis'
  }
};

world.dig('locations', 'usa');
// => 'Indianapolis'

world.dig('locations', 'asia', 'japan');
// => 'null'

or a little more complicated:

const germany = () => 'germany';
const world = [0, 1, { location: { europe: germany } }, 3];
world.dig(2, 'location', 'europe') === germany;
world.dig(2, 'location', 'europe')() === 'germany';
Devchris
  • 394
  • 3
  • 12
0

?? would work in js which is equivalent to ?: in kotlin

Vibin
  • 155
  • 1
  • 4
-8

Personally i use

function e(e,expr){try{return eval(expr);}catch(e){return null;}};

and for example safe get:

var a = e(obj,'e.x.y.z.searchedField');
  • 4
    First of you [really shouldn't use eval](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#Don't_use_eval_needlessly!). Secondly this doesn't even work: `e({a:{b:{c:{d:'test'}}}}, 'a.b.c.d')` returns `null`. – Pylinux Jan 10 '17 at 14:04
  • @Pylinux basically what would work is `e = eval`, `var a = eval('obj.a.b.c.d')`. `eval` doesn't even take a second parameter... https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval – Dorian Jan 22 '17 at 05:16