Destructuring and Default Parameters in ES6 - Redfin Real Estate News

Destructuring and Default Parameters in ES6

by
Updated on October 5th, 2020

Today, I was reading this React Server pull request from the inimitable Sasha Aickin to our awesome new open source React server, cleverly named react-server when I came across this function signature

export default (routes, {
  workingDir = ‘./__clientTemp’,
  routesDir = ‘.’,
  ...
} = {}) => {
  // function body omitted
}

I generally like to think that I’m a pretty good JavaScript dev, and that I’ve been doing a good job keeping up with ES6, but I was completely lost on this one.  I was pretty sure that this was destructuring a default argument, but based on the MDN documentation at the time, I would have expected this to look like this

export default (routes, {
  workingDir: workingDir,
  routesDir: routesDir} = {
  workingDir: ‘./__clientTemp’,
  routesDir: ‘.’
}) {
  // function body omitted
}

(note that MDN has since updated their documentation). Other than the fact that I’m not sure that’s the most readable indentation, I was fairly certain that would work, but when I babelified Sasha’s implementation, I got code that looked exactly like what I would have expected

sasha.js

export default (routes, {
  workingDir = './__clientTemp',
  routesDir = '.',
} = {}) => {
  console.log(workingDir);
  console.log(routesDir);
}

dougwade ~/temp » babel sasha.js

'use strict';
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = function (routes) {
  var _ref = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
  var _ref$workingDir = _ref.workingDir;
  var workingDir = _ref$workingDir === undefined ? './__clientTemp' : _ref$workingDir;
  var _ref$routesDir = _ref.routesDir;
  var routesDir = _ref$routesDir === undefined ? '.' : _ref$routesDir;
  console.log(workingDir);
  console.log(routesDir);
};
module.exports = exports['default'];

And even more interesting was how unlike what I thought MDN was telling me I should do it was

mdn.js

export default (routes, {
  workingDir: workingDir,
  routesDir: routesDir,
} = {
  workingDir: './__clientTemp',
  routesDir: '.',
}) => {
  console.log(workingDir);
  console.log(routesDir);
}

dougwade ~/temp » babel mdn.js

'use strict';
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = function (routes) {
  var _ref = arguments.length <= 1 || arguments[1] === undefined ? {
    workingDir: './__clientTemp',
    routesDir: '.'
  } : arguments[1];
  var workingDir = _ref.workingDir;
  var routesDir = _ref.routesDir;
  console.log(workingDir);
  console.log(routesDir);
};

And how much better behaved it is than what I was trying to do

harness.js

import destruct from "./sasha" //changed to “./mdn” for mdnHarness.js
let initialParam = {unused: true};
console.log('only one function param');
destruct(initialParam);
console.log('pass a well-behaved second argument');
destruct(initialParam, {
	workingDir: './foo',
	routesDir: './bar',
})
console.log('pass a partial second argument; hopefully routesDir is “.”');
destruct(initialParam, {
	workingDir: './foo',
});
console.log('expliticly pass undefined');
destruct(initialParam);

terminal

dougwade ~/temp » babel --presets es2015 --plugins add-module-exports mdn.js > target/mdn.js
dougwade ~/temp » babel --presets es2015 --plugins add-module-exports sasha.js > target/sasha.js
dougwade ~/temp » babel --presets es2015 --plugins add-module-exports harness.js > sashaHarness.js
# editing goes here
dougwade ~/temp » babel --presets es2015 --plugins add-module-exports harness.js > mdnHarness.js

dougwade ~/temp » node target/sashaHarness.js

only one function param
./__clientTemp
.
pass a well-behaved second argument
./foo
./bar
pass a partial second argument
./foo
.
explicitly pass undefined
./__clientTemp
.
dougwade ~/temp » node target/mdnHarness.js
only one function param
./__clientTemp
.
pass a well-behaved second argument
./foo
./bar
pass a partial second argument
./foo
undefined
expliticly pass undefined
./__clientTemp
.

Note especially the case of a partial second argument — when you pass a partial argument to what I thought MDN was suggesting, the second argument is undefined, whereas in Sasha’s solution, you still get the default behavior.  So, what’s going on; what sorcery is making this do?

My first thought was that I misunderstood destructuring, but there seemed to be no indication that { x = 1, y = 2 } = {} was valid destructuring from MDN (in case you hadn’t noticed, MDN is my go-to reference for how you JavaScript).  My next thought was that maybe I didn’t understand the object literal, but that didn’t seem to have any hints either, and the default parameters article is what had gotten me into this mess in the first place.  My first real clue came from the eminent Dr Axel Rauschmayer from his blog post Destructuring Algorithm, which has this example [{x=0, y=0} = {}] <- [{z:3}].  Turns out we’re in what Dr. Raushmayer calls rule 2d {key: pattern = default_value, <<properties>>}, and sure enough, when I dropped into the node repl, everything worked just as I expected with my new-found enlightenment

So, what happened to me?  Check out this editorial aside in the Mozilla hacks article on destructuring

temp.js

let {
  workingDir = './__clientTemp',
} = { routesDir: '.' };
console.log(workingDir);
console.log(routesDir);

terminal

dougwade ~/temp » babel temp.js > target/temp.js
dougwade ~/temp » node target/temp.js
./__clientTemp
/Users/doug.wade/temp/target/temp.js:8
console.log(routesDir);
           ^
ReferenceError: routesDir is not defined
   at Object.<anonymous> (/Users/doug.wade/temp/target/temp.js:8:13)
   at Module._compile (module.js:413:34)
   at Object.Module._extensions..js (module.js:422:10)
   at Module.load (module.js:357:32)
   at Function.Module._load (module.js:314:12)
   at Function.Module.runMain (module.js:447:10)
   at startup (node.js:140:18)
   at node.js:1001:3

(Editor’s note: This feature is currently implemented in Firefox only for the first two cases, not the third. See bug 932080.)

The third example referenced looks like this

var { x = 3 } = {};
console.log(x);
// 3

My takeaway, then, is a cautionary tale.  For me, MDN is far and away the most accessible, attractive, and well-written source of documentation for JavaScript, but it’s not intended to be the authoritative source on the ECMAScript standard, which is what Chrome and Node and Babel develop against, but instead the authoritative source on how Firefox and SpiderMonkey function.  If you want to know what is in the ECMAScript standard, probably your best starting place is Exploring ES6.

Avatar

Doug Wade

I'm a software developer at Redfin on the Platforms team. I'm a cyclist, soccer hooligan, and beer enthusiast.

Email Doug

Be the first to see the latest real estate news:

  • This field is for validation purposes and should be left unchanged.

By submitting your email you agree to Redfin’s Terms of Use and Privacy Policy

Scroll to Top