Function parameter definitions in ES6

What I don’t understand is how this relates to destructuring.

Within removeBreakpoint, you can use url, line, and column directly. The destructuring happens when removeBreakpoint is called with an options object; that object’s matching properties are destructured into individual arguments.

Is the idea that you permit the ability to pass an object into this function that can be in arbitrary order as long as it contains all items, i.e. { line: 10, column: 20, url: ‘localhost’ }?

Yes, but it doesn’t have to contain all the items; if it doesn’t, then since the argument is initialized from an object property that doesn’t exist, the argument is undefined (unless a default value is specified).

Simple example demonstrating the destructuring (Live Copy with ES5 translation on Babel’s REPL):

"use strict";
function removeBreakpoint({ url, line, column }) {   
    console.log("removeBreakpoint:");
    console.log("url: " + url);
    console.log("line: " + line);
    console.log("column: " + column);
}
removeBreakpoint({
  url: "the url",
  line: "the line",
  column: "the column"
});
removeBreakpoint({
  url: "the url",
  line: "the line"
});

Output:

removeBreakpoint:
url: the url
line: the line
column: the column
removeBreakpoint:
url: the url
line: the line
column: undefined

If so, what is the benefit over something like

function removeBreakpoint(params) {   
   // ... 
}

where params is an object with url, line, and column?

Syntactic sugar. The new syntax for accepting options objects is more concise and declarative, automating a common pattern. This is particularly apparent when you combine it with default values (Live Copy):

"use strict";
function removeBreakpoint(
    {                               // <= { starts destructuring arg
        url = "url default",        // <= Default for `url`
        line = "line default",      // <= ...for `line`
        column = "column default"   // <= ...for `column`
    }                               // <= } ends destructuring arg
    = {}                            // <= Default for the options object iself
) {                                 //    (see notes after the code block)
    console.log("removeBreakpoint:");
    console.log(url);
    console.log(line);
    console.log(column);
}
removeBreakpoint({
  url: "the url",
  line: "the line",
  column: "the column"
});
removeBreakpoint({
  url: "the url",
  line: "the line"
});
removeBreakpoint();

Output:

removeBreakpoint:
the url
the line
the column
removeBreakpoint:
the url
the line
column default
removeBreakpoint:
url default
line default
column default

In the above, even the options object itself is optional, which is why the last call works:

removeBreakpoint();

If we hadn’t given a default for the options object itself, that call would have failed because we’d be trying to read the property url of undefined. Sometimes you want that, and so you’d leave the overall option off. Other times you don’t.


Side note: The ability to default parts of the options object and also, separately, the entire options object leads to an interesting situation where you can have different defaults depending on whether an options object was given but didn’t have a specific option vs. no options object being given at all, all done declaratively: Live Copy

"use strict";
function test(
    num,
    {
        foo = "foo default",
        bar = "options given without bar"
    } = {bar: "options not given at all"}
) {
    console.log(num + ": foo = " + foo + ", bar = " + bar);
}
test(1, {foo: "foo value", bar: "options given with bar"});
test(2, {bar: "options given with bar"});
test(3, {});
test(4);

Output:

1: foo = foo value, bar = options given with bar
2: foo = foo default, bar = options given with bar
3: foo = foo default, bar = options given without  bar
4: foo = foo default, bar = options not given at all

Leave a Comment