202

I know that I can test for a JavaScript variable and then define it if it is undefined, but is there not some way of saying

var setVariable = localStorage.getItem('value') || 0;

seems like a much clearer way, and I'm pretty sure I've seen this in other languages.

Zsolt Meszaros
  • 8,506
  • 12
  • 20
  • 30
pedalpete
  • 19,318
  • 41
  • 115
  • 225
  • 31
    that is _not_ a test for "undefined", it's a test for "falsey" – Alnitak Mar 23 '11 at 18:19
  • 3
    Note that `localStorage.getItem()` will throw an exception if the user has disabled cookies (at least in Chrome), so you may want to wrap it inside a `try...catch` clause – urish May 10 '14 at 08:10
  • 2
    Possible duplicate of [What does the construct x = x || y mean?](http://stackoverflow.com/questions/2802055/what-does-the-construct-x-x-y-mean) – Michał Perłakowski Apr 09 '16 at 23:15

12 Answers12

338

Yes, it can do that, but strictly speaking that will assign the default value if the retrieved value is falsey, as opposed to truly undefined. It would therefore not only match undefined but also null, false, 0, NaN, "" (but not "0").

If you want to set to default only if the variable is strictly undefined then the safest way is to write:

var x = (typeof x === 'undefined') ? your_default_value : x;

On newer browsers it's actually safe to write:

var x = (x === undefined) ? your_default_value : x;

but be aware that it is possible to subvert this on older browsers where it was permitted to declare a variable named undefined that has a defined value, causing the test to fail.

Alnitak
  • 313,276
  • 69
  • 379
  • 466
  • 3
    I'm surprised this pattern isn't included in more JS libs. – joemaller Feb 15 '13 at 19:25
  • is there a downside to using `var x = (x === undefined ? def_val : x);` as the slightly longer `var x = (typeof x === 'undefined') ? def_val : x;`. Does the behavior differ? – Marco Pashkov Feb 25 '14 at 17:45
  • 4
    @marco in older browsers it was possible for `undefined` to be redefined, causing the equality test to fail. – Alnitak Feb 26 '14 at 09:23
  • @Alnitak what is the best approach to use a similar syntax to the one that OP is using and still check for undefined? – Ivo Pereira Dec 23 '14 at 11:03
  • @IvoPereira You can't use the `||` approach in a strict test for undefined, you have to use an explicit test for `undefined` with a conditional. – Alnitak Dec 23 '14 at 11:05
  • It does the trick but it is economically not advantageous as you have to write the variable name three times. an if ( x==undefined) {x = def_val} is clear and requires only one repetition. I think it is really worth adding a compact way. Something like |= (or any other convenient symbol to pair with the = meaning: assign if not already defined) – a1an Apr 04 '16 at 13:22
  • @a1an other languages have a "null coalescing operator" that doesn't have the (often unwanted) behaviour of `||=` where it matches any "falsey" value, and not just strictly "undefined" values. JS unfortunately doesn't. – Alnitak Apr 04 '16 at 14:26
  • @Alnitak is there a list of those older browser somewhere? – dieresys May 17 '16 at 21:04
  • The following option is better `var x = (x === void 0) ? def_val : x;` just because it's shorter. – Victor Dombrovsky Jan 05 '17 at 14:23
  • Using `void 0` instead of `undefined` works around the old browser issue you pointed out. – Trevor Dixon Jul 13 '18 at 12:34
37

Logical nullish assignment, ES2020+ solution

New operators are currently being added to the browsers, ??=, ||=, and &&=. This post will focus on ??=.

This checks if left side is undefined or null, short-circuiting if already defined. If not, the right-side is assigned to the left-side variable.

Comparing Methods

// Using ??=
name ??= "Dave"

// Previously, ES2020
name = name ?? "Dave"

// Before that (not equivalent, but commonly used)
name = name || "Dave" // name ||= "Dave"

Basic Examples

let a       // undefined
let b = null
let c = false

a ??= true  // true
b ??= true  // true
c ??= true  // false

// Equivalent to
a = a ?? true

Object/Array Examples

let x = ["foo"]
let y = { foo: "fizz" }

x[0] ??= "bar"  // "foo"
x[1] ??= "bar"  // "bar"

y.foo ??= "buzz"  // "fizz"
y.bar ??= "buzz"  // "buzz"

x  // Array [ "foo", "bar" ]
y  // Object { foo: "fizz", bar: "buzz" }

??= Browser Support Mar 2021 - 83%

??= Mozilla Documentation

||= Mozilla Documentation

&&= Mozilla Documentation

Gibolt
  • 24,018
  • 9
  • 129
  • 89
  • 1
    Is it really a good idea to copy this answer to two other questions ([Replace a value if null or undefined in JavaScript](https://stackoverflow.com/a/62824506/4642212) and [Is there a “null coalescing” operator in JavaScript?](https://stackoverflow.com/a/62824349/4642212))? Woudln’t it be better to VTC as duplicate or link to related questions in the comments? – Sebastian Simon Jul 11 '20 at 21:22
  • The questions are all slightly different; as such so are the answers. The examples are consistent to improve inline context at a glance. Probably could have linked for more detail, but would have required an extra hop that may cause people to skip the solution – Gibolt Jul 11 '20 at 21:39
  • 1
    Note that some IDEs (NetBeans) detect `??=` as invalid syntax. Default auto source formatting will convert it to `?? =` with a space in between, which is invalid JS. Now to figure out how to customize the auto-formatter... – OXiGEN Sep 16 '20 at 09:00
  • can't believe this isn't already supported, such a basic feature – samthet Nov 13 '20 at 11:56
30

The 2018 ES6 answer is:

return Object.is(x, undefined) ? y : x;

If variable x is undefined, return variable y... otherwise if variable x is defined, return variable x.

Sterling Bourne
  • 2,390
  • 20
  • 20
  • 4
    As far as I can tell, `Object.is` isn't any better than `===` in this case. "The === operator (and the == operator as well) treats the number values -0 and +0 as equal and treats Number.NaN as not equal to NaN." (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) Doesn't matter here. – Trevor Dixon Jul 13 '18 at 12:33
  • Even so, Trevor, you should always use Object.is() now instead of === as a force of habit. – Sterling Bourne Jul 13 '18 at 14:02
  • 6
    I disagree. Know them both and use the appropriate one. In particular, use `Object.is` if both of the operands might be `NaN`. – Trevor Dixon Jul 15 '18 at 06:53
  • 3
    If Object.is() works on 100% of the use cases, and === works on 98% of the use cases, why would you risk using === at all? For example: console.log(+0 === -0); // outputs true, while console.log(Object.is(+0, -0)); // outputs false -- which is better? Is negative 0 the same as positive 0? That's the question you have to ask yourself, but if you use Object.is(), you always know the answer. They are not the same, so an output of false is the correct and expected result. – Sterling Bourne Jul 18 '18 at 15:07
  • 2
    The particular context here is always comparing to `undefined`. So `===` will work 100% of the time, not just 98%. The advantage of `===` being it is easier to read, especially at a glance, shorter and avoids an unnecessary method call. Personally I would only use `Object.is()` when the situation requires it and prefer `===` in all other cases. – blubberdiblub Apr 05 '19 at 02:36
  • 1
    Absolutely; if you're an experienced coder. Unless you accidentally forget an equal sign, in which case debugging why == isn't working somewhere is something I would never wish upon any developer. Better safe than sorry and always use Object.is(), The reason is was included in the spec was specifically to help developers write less buggy code. – Sterling Bourne Oct 29 '19 at 14:27
12

ES2020 Answer

With the Nullish Coalescing Operator, you can set a default value if value is null or undefined.

const setVariable = localStorage.getItem('value') ?? 0;

However, you should be aware that the nullish coalescing operator does not return the default value for other types of falsy value such as 0 and ''.

However, do take note of the browser support. You may need to use a JavaScript compiler like Babel to convert it into something more backward compatible. If you are using Node.js, it has been supported since version 14.

wentjun
  • 27,425
  • 7
  • 54
  • 65
11

I needed to "set a variable if undefined" in several places. I created a function using @Alnitak answer. Hopefully it helps someone.

function setDefaultVal(value, defaultValue){
   return (value === undefined) ? defaultValue : value;
}  

Usage:

hasPoints = setDefaultVal(this.hasPoints, true);
Mcestone
  • 746
  • 2
  • 9
  • 24
6

It seems more logical to check typeof instead of undefined? I assume you expect a number as you set the var to 0 when undefined:

var getVariable = localStorage.getItem('value');
var setVariable = (typeof getVariable == 'number') ? getVariable : 0;

In this case if getVariable is not a number (string, object, whatever), setVariable is set to 0

Bruno
  • 749
  • 10
  • 17
4

In our days you actually can do your approach with JS:

// Your variable is null
// or '', 0, false, undefined
let x = null;

// Set default value
x = x || 'default value';

console.log(x); // default value

So your example WILL work:

const setVariable = localStorage.getItem('value') || 0;
tarkh
  • 1,429
  • 1
  • 5
  • 8
2

If you're a FP (functional programming) fan, Ramda has a neat helper function for this called defaultTo :

usage:

const result = defaultTo(30)(value)

It's more useful when dealing with undefined boolean values:

const result2 = defaultTo(false)(dashboard.someValue)

Damian Green
  • 4,887
  • 1
  • 25
  • 32
  • 1
    this is exactly `const result = value || 30;`, except is using an in-between function and +1000 bytes lib – Adelin Dec 17 '18 at 11:10
  • 1
    @Adelin not true, it also handles falsy for boolean types. If you're using this lib already (as I am) then it's pretty useful, and terse – Damian Green Dec 17 '18 at 11:24
1

Ran into this scenario today as well where I didn't want zero to be overwritten for several values. We have a file with some common utility methods for scenarios like this. Here's what I added to handle the scenario and be flexible.

function getIfNotSet(value, newValue, overwriteNull, overwriteZero) {
    if (typeof (value) === 'undefined') {
        return newValue;
    } else if (value === null && overwriteNull === true) {
        return newValue;
    } else if (value === 0 && overwriteZero === true) {
        return newValue;
    } else {
        return value;
    }
}

It can then be called with the last two parameters being optional if I want to only set for undefined values or also overwrite null or 0 values. Here's an example of a call to it that will set the ID to -1 if the ID is undefined or null, but wont overwrite a 0 value.

data.ID = Util.getIfNotSet(data.ID, -1, true);
Mark Seefeldt
  • 552
  • 4
  • 9
1

var setVariable = (typeof localStorage.getItem('value') !== 'undefined' && localStorage.getItem('value')) || 0;

Zikes
  • 5,808
  • 1
  • 28
  • 44
0

Works even if the default value is a boolean value:

var setVariable = ( (b = 0) => b )( localStorage.getItem('value') );
user
  • 5,816
  • 7
  • 53
  • 105
Tsz Lam Yau
  • 1
  • 1
  • 1
0

It seems to me, that for current javascript implementations,

var [result='default']=[possiblyUndefinedValue]

is a nice way to do this (using object deconstruction).

T S
  • 1,128
  • 10
  • 19