This article describes how to parse the deconstruct assignment in the JavaScript ES6 version. The ES6 version has brought many simplified improvements to JS. If you need it, What Is deconstruct assignment?
Deconstruct value assignment allows you to assign values of arrays and objects to a series of variables using syntax similar to arrays or object literal values. This syntax is concise and clearer than traditional attribute access.
Without using deconstruct assignment, access the first three items of the array:
var first = someArray[0];var second = someArray[1];var third = someArray[2]; var first = someArray[0];var second = someArray[1];var third = someArray[2];
After the deconstruct assignment is used, the corresponding code becomes more concise and readable:
var [first, second, third] = someArray; var [first, second, third] = someArray;
SpiderMonkey (JavaScript Engine of Firefox) has supported most of the features of deconstruct assignment, but it is not completely.
Deconstruct and assignment of arrays and iteratable objects
We have seen an example of array deconstruct assignment. The syntax is generally in the following format:
[ variable1, variable2, ..., variableN ] = array; [ variable1, variable2, ..., variableN ] = array;
This assigns the corresponding items in the array to variable1 to variableN in sequence. If you need to declare the variables at the same time, you can add the var, let or const keyword before the deconstruct expression.
var [ variable1, variable2, ..., variableN ] = array;let [ variable1, variable2, ..., variableN ] = array;const [ variable1, variable2, ..., variableN ] = array; var [ variable1, variable2, ..., variableN ] = array;let [ variable1, variable2, ..., variableN ] = array;const [ variable1, variable2, ..., variableN ] = array;
In fact, you can also nest any depth:
var [foo, [[bar], baz]] = [1, [[2], 3]];console.log(foo);// 1console.log(bar);// 2console.log(baz);// 3 var [foo, [[bar], baz]] = [1, [[2], 3]];console.log(foo);// 1console.log(bar);// 2console.log(baz);// 3
In addition, you can skip some items in the array:
var [,,third] = ["foo", "bar", "baz"];console.log(third);// "baz" var [,,third] = ["foo", "bar", "baz"];console.log(third);// "baz"
You can also use a Rest expression to capture the remaining items in the array:
var [head, ...tail] = [1, 2, 3, 4];console.log(tail);// [2, 3, 4] var [head, ...tail] = [1, 2, 3, 4];console.log(tail);// [2, 3, 4]
If the array is out of bounds or the entry that does not exist in the array is accessed, the same value is obtained: undefined.
console.log([][0]);// undefinedvar [missing] = [];console.log(missing);// undefined console.log([][0]);// undefined var [missing] = [];console.log(missing);// undefined
Note that the array deconstruct assignment method also applies to objects that can be traversed:
function* fibs() { var a = 0; var b = 1; while (true) { yield a; [a, b] = [b, a + b]; }}var [first, second, third, fourth, fifth, sixth] = fibs();console.log(sixth);// 5 function* fibs() { var a = 0; var b = 1; while (true) { yield a; [a, b] = [b, a + b]; }} var [first, second, third, fourth, fifth, sixth] = fibs();console.log(sixth);// 5
Deconstruct and assignment of Objects
The deconstruct value assignment of objects allows you to bind variables to different property values of objects. Specify the name of the property to be bound, followed by the variable to be bound:
var robotA = { name: "Bender" };var robotB = { name: "Flexo" };var { name: nameA } = robotA;var { name: nameB } = robotB;console.log(nameA);// "Bender"console.log(nameB);// "Flexo" var robotA = { name: "Bender" };var robotB = { name: "Flexo" }; var { name: nameA } = robotA;var { name: nameB } = robotB; console.log(nameA);// "Bender"console.log(nameB);// "Flexo"
When the bound property name is the same as the variable name of the received property value, there is also a syntax SUGAR:
var { foo, bar } = { foo: "lorem", bar: "ipsum" };console.log(foo);// "lorem"console.log(bar);// "ipsum" var { foo, bar } = { foo: "lorem", bar: "ipsum" };console.log(foo);// "lorem"console.log(bar);// "ipsum"
Like arrays, they can also be nested:
var complicatedObj = { arrayProp: [ "Zapp", { second: "Brannigan" } ]};var { arrayProp: [first, { second }] } = complicatedObj;console.log(first);// "Zapp"console.log(second);// "Brannigan" var complicatedObj = { arrayProp: [ "Zapp", { second: "Brannigan" } ]}; var { arrayProp: [first, { second }] } = complicatedObj; console.log(first);// "Zapp"console.log(second);// "Brannigan"
Undefined is obtained when a nonexistent attribute is reconstructed:
var { missing } = {};console.log(missing);// undefined var { missing } = {};console.log(missing);// undefined
There is also a potential trap when using the object's deconstruct value assignment. No variables are declared during the deconstruct value assignment (no var, let, or const keywords ):
{ blowUp } = { blowUp: 10 };// Syntax error { blowUp } = { blowUp: 10 };// Syntax error
This is because the JavaScript syntax tells the engine that any statement starting with {is a statement block (for example, {console} is a legal statement block). The solution is to wrap the entire statement in a pair of parentheses:
({ safe } = {});// No errors ({ safe } = {});// No errors
Other cases
When you try to deconstruct null or undefined, you will get the type error:
var {blowUp} = null;// TypeError: null has no properties var {blowUp} = null;// TypeError: null has no properties
However, you can deconstruct other basic types (Boolean, String, and Number) to get undefined:
var {wtf} = NaN;console.log(wtf);// undefined var {wtf} = NaN;console.log(wtf);// undefined
The results may surprise you, but the reason is very simple. During Object deconstruct assignment, the deconstruct Object is forcibly converted to an Object. Except null and undefined, other types can be forcibly converted to an Object. When assigning values to the structure of an array, a traversal is required for the deconstruct object.
Default Value
You can specify a default value for a property that does not exist:
var [missing = true] = [];console.log(missing);// truevar { message: msg = "Something went wrong" } = {};console.log(msg);// "Something went wrong"var { x = 3 } = {};console.log(x);// 3 var [missing = true] = [];console.log(missing);// true var { message: msg = "Something went wrong" } = {};console.log(msg);// "Something went wrong" var { x = 3 } = {};console.log(x);// 3
Practical Application
Function Parameters
As developers, we often use an object that contains multiple attributes as function parameters to implement more flexible APIs, rather than letting API users remember parameters in specific sequence. We can use the deconstruct value assignment of objects to avoid attribute access every time parameters are used:
function removeBreakpoint({ url, line, column }) { // ...} function removeBreakpoint({ url, line, column }) { // ...}
Configuration object
In the preceding example, we can provide default values for the object attributes to be deconstruct, which is very useful for the objects used as configuration parameters, because many configuration items have a reasonable default value. For example, the second parameter of jQuery's ajax method is a configuration object, which can be implemented as follows:
jQuery.ajax = function (url, { async = true, beforeSend = noop, cache = true, complete = noop, crossDomain = false, global = true, // ... more config}) { // ... do stuff}; jQuery.ajax = function (url, { async = true, beforeSend = noop, cache = true, complete = noop, crossDomain = false, global = true, // ... more config}) { // ... do stuff};
This avoids repeated code like this: var foo = config. foo | theDefaultFoo ;.
Used with the iterator
When traversing a Map object, we can use the deconstruct value assignment to traverse [key, value]:
var map = new Map();map.set(window, "the global");map.set(document, "the document");for (var [key, value] of map) { console.log(key + " is " + value);}// "[object Window] is the global"// "[object HTMLDocument] is the document" var map = new Map();map.set(window, "the global");map.set(document, "the document"); for (var [key, value] of map) { console.log(key + " is " + value);}// "[object Window] is the global"// "[object HTMLDocument] is the document"
Traverse Key only:
For (var [key] of map ){//...} for (var [key] of map ){//...} traverse only values: for (var [, value] of map ){//...} for (var [, value] of map ){//...}
Returns multiple values.
Return an array and extract the returned value through deconstruct Value assignment:
function returnMultipleValues() { return [1, 2];}var [foo, bar] = returnMultipleValues(); function returnMultipleValues() { return [1, 2];}var [foo, bar] = returnMultipleValues();
Or, return the object of a key-Value Pair:
function returnMultipleValues() { return { foo: 1, bar: 2 };}var { foo, bar } = returnMultipleValues(); function returnMultipleValues() { return { foo: 1, bar: 2 };}var { foo, bar } = returnMultipleValues();
Both are better than using intermediate variables:
function returnMultipleValues() { return { foo: 1, bar: 2 };}var temp = returnMultipleValues();var foo = temp.foo;var bar = temp.bar; function returnMultipleValues() { return { foo: 1, bar: 2 };}var temp = returnMultipleValues();var foo = temp.foo;var bar = temp.bar;
Continuation:
function returnMultipleValues(k) { k(1, 2);}returnMultipleValues((foo, bar) => ...); function returnMultipleValues(k) { k(1, 2);}returnMultipleValues((foo, bar) => ...);
Import the specified part of the CommonJS Module
I haven't used ES6 modules yet, so I have used CommonJS at least. When importing a CommonJS module X, the methods provided by the module may be unnecessary. Using deconstruct assignment, you can specify the parts of the module you need:
const { SourceMapConsumer, SourceNode } = require("source-map"); const { SourceMapConsumer, SourceNode } = require("source-map");
If you use the ES6 module mechanism, you can see that the import declaration has a similar syntax.
Conclusion
We can see that deconstruct assignment is useful in many scenarios. At Mozilla, we already have a lot of experience. Lars Hansen introduced the deconstruct assignment to Opera 10 years ago. Brendan Eich also added support to Firefox a little later, which first appeared in Firefox 2. Therefore, deconstruct value assignment has penetrated into our daily use of JS, quietly making our code shorter and cleaner.