JavaScript has an operator called the default operator. Never heard of this? Basically, it's just the Logical OR operator, ||. It's a boolean operator, short-circuited, and evaluate to the last evaluated term. With this short-circuit effect and evaluation rule, we can make use of it to give variables and object properties default values, even to provide behavior when no truthy value is available. Thus called the default operator.

To be exact, we can use the Logical OR operator to give a falsy value/variable/object property a default/fallback value, and to provide behavior when no truthy value is available. And falsy values are boolean false, number 0, number NaN, string "", undefined undefined and object null.

Usage of the default operator is like the null coalescing operator ?? in C#, COALESCE function in SQL, with the difference that JavaScript default operator applies to all falsy values, while null coalescing operators in other languages apply to null only. See Null coalescing operator - Wikipedia for details.

Here is an example of using default operator from Google JavaScript Style Guide:

We can write this:

/** @param {*=} opt_win */
function foo(opt_win) {
  var win = opt_win || window;
  // ...
}

instead of this:

/** @param {*=} opt_win */
function foo(opt_win) {
  var win;
  if (opt_win) {
    win = opt_win;
  } else {
    win = window;
  }
  // ...
}

We may find that with the default operator, we can write conciser and easier-to-read code.

Though some may argue that this technique is not intuitive to those who have not learnt this. But if you have not got yourself familiar with this technique, you should do so, as this technique is used extensively in JavaScript world, and has been utilized in many scripts, libraries and frameworks.

Examples

Here are some real life examples using the default operator.

Using default operator to provide a default/fallback value

Examples in Underscore.js

L168, L192, L242, L255:

        length = (keys || obj).length;

L492:

    for (var i = startIndex || 0, length = getLength(input); i < length; i++) {

L683:

    step = step || 1;

L1147:

    aStack = aStack || [];
    bStack = bStack || [];

And L589, L680, L912, L1420, L1447, L1454, L1465.

Examples in Backbone.js

L31:

    root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$));

L175:

      var listeners = obj._listeners || (obj._listeners = {});

L188:

    var listeningTo = this._listeningTo || (this._listeningTo = {});

L396: L475: L755: L997: L1437:

    options || (options = {});

L1069:

      return attrs[this.model.prototype.idAttribute || 'id'];

L1600:

      var match = (window || this).location.href.match(/#(.*)$/);

L1652: L1779:

         var root = this.root.slice(0, -1) || '/';

And L12, L168, L187, L194, L206, L210, L395, L400, L725, L970, L1272, L1360, L1376, L1525, L1774.

Examples in Chromium

local_ntp.js L542

  var innerWidth = window.innerWidth || maxSnapSize;

Using default operator to polyfill native functions

Examples in Underscore.js

L1207:

  _.isArray = nativeIsArray || function(obj) {
    return toString.call(obj) === '[object Array]';
  };

L1331:

  _.now = Date.now || function() {
    return new Date().getTime();
  };

Examples in Backbone.js

L1681:

      // Add a cross-platform `addEventListener` shim for older browsers.
      var addEventListener = window.addEventListener || function (eventName, listener) {
        return attachEvent('on' + eventName, listener);
      };

L1702:

      // Add a cross-platform `removeEventListener` shim for older browsers.
      var removeEventListener = window.removeEventListener || function (eventName, listener) {
        return detachEvent('on' + eventName, listener);
      };

Using default operator to throw error when no appropriate values are available

Examples in Backbone.js

L690:

      var base =
        _.result(this, 'urlRoot') ||
        _.result(this.collection, 'url') ||
        urlError();

L1370:

      params.url = _.result(model, 'url') || urlError();

L1878 contains implementation for function urlError:

  // Throw an error when a URL is needed, and none is supplied.
  var urlError = function() {
    throw new Error('A "url" property or function must be specified');
  };

The two styles of using default operator in assignment

We may find in examples given above that when using default operator for assignment to object properties, two different styles are applied.

One is to assign the or-ed result,

this.foo = this.foo || {};

And the other is to do or-logic first, then do assignment if necessarily.

this.foo || (this.foo = {});

These two styles are equivalent as per the result. And the first one requires two less characters than the second one (the omitted parentheses, which is required in the second one due to operator precedence). But if there is any performance difference? I've come up with the following jsperf test to help get a rough idea on this.

Performance comparison

Here is the performance comparison on using the two different styles mentioned above and using plain if to give a default value.

See this jsperf: http://jsperf.com/default-operator/3

And my test result:

default operator performance test

We can conclude that using these two styles, and using plain if are equally fast. So to use which one is just of personal choice.