ES6 allows extracting values from arrays and objects and assigning values to variables according to certain patterns. This is called Destructuring. Prior to ES6, when assigning a value to a variable, you can only specify the value directly.
let a = 1; let b = 2; let c = 3;
ES6 can rewrite the above assignment expression into the following format, extract the value from the array, and assign the variable according to the corresponding position.
let [a, b, c] = [1, 2, 3];
Deconstruction is essentially "pattern matching". As long as the patterns on both sides of the equal sign are the same, the variable on the left will be assigned the corresponding value.
//Array can have nesting relationship let [foo, [[bar], baz]] = [1, [[2], 3]]; foo//1 bar//2 baz//3 //can be omitted let [,, third] = ["foo", "bar", "baz"]; third//"baz" let [x,, y] = [1, 2, 3]; x//1 y//3 let [head, ...tail] = [1, 2, 3, 4]; head//1 tail//[2, 3, 4] let [x, y, ...z] = ['a']; x//"a" y//undefined z//[]
If the deconstruction is unsuccessful, the value of the variable is equal to undefined
.
let [foo] = []; let [bar, foo] = [1];
Another case is incomplete deconstruction, that is, the pattern on the left side of the equal sign only matches a part of the array on the right side of the equal sign. In this case, deconstruction can still be successful.
let [x, y] = [1, 2, 3]; x//1 y//2 let [a, [b], d] = [1, [2, 3], 4]; a//1 b//2 d//4
For the Set structure, you can also use the destructuring assignment of the array.
let [x, y, z] = new Set(['a','b','c']); x//"a"
In fact, as long as a certain data structure has an Iterator interface, it can be deconstructed and assigned in the form of an array.
function* fibs() { let a = 0; let b = 1; while (true) { yield a; [a, b] = [b, a + b]; } } let [first, second, third, fourth, fifth, sixth] = fibs(); sixth//5
In the above code, it fibs
is a Generator function, which has an Iterator interface natively. Destructuring assignment will in turn obtain values from this interface.
Destructuring assignment allows default values to be specified.
let [foo = true] = []; foo//true let [x, y ='b'] = ['a'];//x='a', y='b' let [x, y ='b'] = ['a', undefined];//x='a', y='b'
Note that ES6 uses the strict equality operator ( ===
) internally to determine whether a position has a value. Therefore, the undefined
default value will only take effect when an array member is strictly equal .
let [x = 1] = [undefined]; x//1 let [x = 1] = [null]; x//null
In the above code, if an array member is null
, the default value will not take effect because it is null
not strictly equal undefined
.
If the default value is an expression, then the expression is evaluated lazily, that is, it will be evaluated only when it is used.
function f() { console.log('aaa'); } let [x = f()] = [1];
In the above code, because x
the value can be retrieved, the function f
will not be executed at all. The above code is actually equivalent to the following code.
let x; if ([1][0] === undefined) { x = f(); } else { x = [1][0]; }
The default value can refer to other variables assigned by destructuring, but the variable must have been declared.
let [x = 1, y = x] = [];//x=1; y=1 let [x = 1, y = x] = [2];//x=2; y=2 let [x = 1, y = x] = [1, 2];//x=1; y=2 let [x = y, y = 1] = [];//ReferenceError: y is not defined
The last expression above will report an error because it has not been declared when it is x
used y
as a default value y
.
The difference between object destructuring and array is. The elements of the array are arranged in order, and the value of the variable is determined by its position; and the object variable must have the same name as the attribute to get the correct value. But it only requires that the names of the variables are consistent, and there is no requirement for the order of the values of the variables
let {bar, foo} = {foo:'aaa', bar:'bbb' }; foo//"aaa" bar//"bbb"
If the variable does not have a corresponding attribute with the same name, the value is yes undefined
.
let {baz} = {foo:'aaa', bar:'bbb' }; baz//undefined
The destructuring assignment of an object can 方法
assign the value of an existing object to a variable. Assign the Math
three methods of absolute value, random value and square root of the object to the corresponding variable
//Example 1 let {abs, random,sqrt:sqrtFun} = Math; console.log(abs(-16),random(), sqrtFun(4))
If the method name and the need are inconsistent sqrt:sqrtFun
, the variable name and the attribute name are the same, and must be written as follows.
let {foo: baz} = {foo:'aaa', bar:'bbb' }; baz//"aaa" let obj = {first:'hello', last:'world' }; let {first: f, last: l} = obj; f//'hello' l//'world'
This actually shows that the destructuring assignment of an object is a shorthand for the following form
let {foo: foo, bar: bar} = {foo:'aaa', bar:'bbb' };
In other words, the internal mechanism of object deconstruction assignment is to find the attribute with the same name first, and then assign it to the corresponding variable. The latter is actually assigned, not the former.
let {foo: baz} = {foo:'aaa', bar:'bbb' }; baz//"aaa" foo//error: foo is not defined
In the above code, foo
the matched pattern baz
is the variable. It is the variable baz
that is actually assigned , not the pattern foo
.
Like arrays, destructuring can also be used for objects with nested structures.
let obj = { p: [ 'Hello', {y:'World'} ] }; let {p: [x, {y }]} = obj; x//"Hello" y//"World"
Note that this p
is a mode, not a variable, so it will not be assigned. If it is p
also assigned as a variable, it can be written as follows.
let obj = { p: [ 'Hello', {y:'World'} ] }; let {p, p: [x, {y }]} = obj; x//"Hello" y//"World" p//["Hello", {y: "World"}]
Here is another example.
const node = { loc: { start: { line: 1, column: 5 } } }; let {loc, loc: {start }, loc: {start: {line }}} = node; line//1 loc//Object {start: Object} start//Object {line: 1, column: 5}
The above code has three destructuring assignment, Dui, respectively loc
, start
, line
deconstruction three attributes assignment. Note that in the last line
deconstruction assignment of attributes, only line
variables were variables, loc
and start
both were patterns, not variables.
The following is an example of nested assignment.
let obj = {}; let arr = []; ({ foo: obj.prop, bar: arr[0]} = {foo: 123, bar: true }); obj//{prop:123} arr//[true]
If the deconstruction mode is a nested object, and the parent attribute of the child object does not exist, an error will be reported.
//report an error let {foo: {bar}} = {baz:'baz'};
In the above code, the foo
attribute of the object on the left side of the equal sign corresponds to a child object. The bar
properties of this sub-object will report an error during deconstruction. The reason is simple, because at foo
this time it is equal to undefined
, and then an error will be reported if the sub-attribute is taken.
Note that the destructuring assignment of objects can take inherited properties.
const obj1 = {}; const obj2 = {foo:'bar' }; Object.setPrototypeOf(obj1, obj2); const {foo} = obj1; foo//"bar"
In the above code, obj1
the prototype object of the object is obj2
. foo
The attribute is not its obj1
own attribute, but the attribute inherited from it obj2
, which can be obtained by destructuring assignment.
Deconstruction of objects can also specify default values.
var {x = 3} = {}; x//3 var {x, y = 5} = {x: 1}; x//1 y//5 var {x: y = 3} = {}; y//3 var {x: y = 3} = {x: 5}; y//5 var {message: msg ='Something went wrong'} = {}; msg//"Something went wrong"
The condition for the default value to take effect is that the attribute value of the object is strictly equal undefined
.
var {x = 3} = {x: undefined}; x//3 var {x = 3} = {x: null}; x//null
In the above code, the attribute x
is equal null
, because it null
is undefined
not strictly equal, so it is a valid assignment, causing the default value to 3
not take effect.
(1) If you want to use a declared variable for destructuring assignment, you must be very careful.
//wrong way let x; {x} = {x: 1}; //SyntaxError: syntax error
The writing of the above code will report an error, because the JavaScript engine will {x}
interpret it as a code block, resulting in a syntax error. This problem can only be solved by not putting the braces at the beginning of the line and avoiding JavaScript interpreting them as code blocks.
//correct writing let x; ({x} = (x: 1});
The above code puts the entire destructuring assignment statement inside a parenthesis, and it can be executed correctly. For the relationship between parentheses and destructuring assignment, see below.
(2) Destructuring assignment allows no variable names to be placed in the pattern on the left side of the equal sign. Therefore, very weird assignment expressions can be written.
({} = [true, false]); ({} ='abc'); ({} = []);
Although the above expression is meaningless, the syntax is legal and can be executed.
(3) Since the nature of the array is a special object, the object property of the array can be destructured.
let arr = [1, 2, 3]; let {0: first, [arr.length-1]: last} = arr; first//1 last//3
The above code deconstructs the array of objects. An array arr
of 0
keys corresponds to a value 1
, [arr.length - 1]
is 2
the key, the corresponding value 3
. Square brackets are "attribute name expressions"
Strings can also be deconstructed and assigned. This is because at this time, the string is converted into an array-like object.
const [a, b, c, d, e] ='hello'; a//"h" b//"e" c//"l" d//"l" e//"o"
Objects like arrays have an length
attribute, so you can also deconstruct and assign values to this attribute.
let {length: len} ='hello'; len//5
When destructuring assignment, if the right side of the equal sign is a numeric value and a Boolean value, it will be converted to an object first.
let {toString: s} = 123; s === Number.prototype.toString//true let {toString: s} = true; s === Boolean.prototype.toString//true
In the above code, the packaging objects of both numeric and boolean values have toString
attributes, so variables s
can take values. The rule of destructuring assignment is that as long as the value on the right side of the equal sign is not an object or an array, it will be converted to an object first. Since undefined
sums null
cannot be converted into objects, destructuring and assigning them will cause an error.
let {prop: x} = undefined;//TypeError let {prop: y} = null;//TypeError
The parameters of the function can also be assigned using destructuring.
function add([x, y]){ return x + y; } add([1, 2]);//3
In the above code, add
the parameter of the function is an array on the surface, but at the moment when the parameter is passed in, the array parameter is deconstructed into a variable x
sum y
. For the code inside the function, the parameters they can feel are x
sum y
.
Here is another example.
[[1, 2], [3, 4]].map(([a, b]) => a + b); //[3, 7]
The destructuring of function parameters can also use default values.
function move({x = 0, y = 0} = {}) { return [x, y]; } move({x: 3, y: 8});//[3, 8] move({x: 3});//[3, 0] move({});//[0, 0] move();//[0, 0]
In the above code, move
the parameter of the function is an object, x
and y
the value of the variable sum is obtained by deconstructing this object . If the deconstruction fails, the x
sum y
equals the default value.
Note that the following writing method will get different results.
function move({x, y} = {x: 0, y: 0 }) { return [x, y]; } move({x: 3, y: 8});//[3, 8] move({x: 3});//[3, undefined] move({});//[undefined, undefined] move();//[0, 0]
The above code is to move
specify default values for the parameters of the function , not for variables x
and y
specify default values, so you will get a different result from the previous way of writing.
undefined
The default value of the function parameter will be triggered.
[1, undefined, 3].map((x ='yes') => x); //[1,'yes', 3]
Although destructuring assignment is very convenient, it is not easy to parse. For the compiler, there is no way to know whether a formula is a pattern or an expression from the beginning. It must be resolved (or cannot be resolved) to know the equal sign. The question that arises is what to do if there are parentheses in the pattern. The rule of ES6 is that parentheses should not be used as long as there is an ambiguity that may lead to deconstruction.
However, this rule is actually not so easy to discern and it is quite troublesome to deal with. Therefore, it is recommended that you do not put parentheses in the pattern whenever possible.
The following three types of destructuring assignments must not use parentheses.
(1) Variable declaration statement
//All errors let [(a)] = [1]; let {x: (c)} = {}; let ({x: c}) = {}; let {(x: c)} = {}; let {(x): c} = {}; let {o: ({ p: p })} = {o: {p: 2} };
The above 6 statements will report errors, because they are all variable declaration statements, and parentheses cannot be used in the pattern.
(2) Function parameters
Function parameters are also variable declarations, so they cannot be enclosed in parentheses.
//report an error function f([(z)]) {return z;} //report an error function f([z,(x)]) {return x;}
(3) Mode of assignment statement
//All errors ({ p: a }) = {p: 42 }; ([a]) = [5];
The above code puts the entire pattern in parentheses, resulting in an error.
//report an error [({ p: a }), {x: c }] = [{}, {}];
The above code puts part of the pattern in parentheses, causing an error.
There is only one situation where you can use parentheses: you can use parentheses in the non-pattern part of an assignment statement.
[(b)] = [3];//correct ({ p: (d)} = ());//correct [(parseInt.prop)] = [3];//correct
The above three lines of statements can be executed correctly, because firstly they are assignment statements, not declaration statements; secondly, their parentheses are not part of the pattern. In the first statement, the pattern is to take the first member of the array, which has nothing to do with the parentheses; in the second statement, the pattern is p
, not d
; the third statement has the same nature as the first statement.
let x = 1; let y = 2; [x, y] = [y, x];
The above code exchanges the values of variables x
and y
values, which is not only concise, but also easy to read, with very clear semantics.
Functions can only return one value. If you want to return multiple values, you can only return them in an array or object. With destructive assignment, it is very convenient to take out these values.
//return an array function example() { return [1, 2, 3]; } let [a, b, c] = example(); //return an object function example() { return { foo: 1, bar: 2 }; } let {foo, bar} = example();
Destructuring assignment can easily associate a set of parameters with variable names.
//The parameter is an ordered set of values function f([x, y, z]) {...} f([1, 2, 3]); //parameter is a set of unordered values function f({x, y, z}) {...} f({z: 3, y: 2, x: 1});
Destructuring assignment is especially useful for extracting data in JSON objects.
let jsonData = { id: 42, status: "OK", data: [867, 5309] }; let {id, status, data: number} = jsonData; console.log(id, status, number); //42, "OK", [867, 5309]
jQuery.ajax = function (url, { async = true, beforeSend = function () {}, cache = true, complete = function () {}, crossDomain = false, global = true, //... more config } = {}) { //... do stuff };
Specify the default value of the parameter to avoid writing in the function bodyvar foo = config.foo || 'default foo';
such a statement .
Any object deployed with the Iterator interface can be usedfor...of
traversed loop. The Map structure natively supports the Iterator interface, and it is very convenient to get the key name and key value with the deconstruction and assignment of variables.
const map = new Map(); map.set('first','hello'); map.set('second','world'); for (let [key, value] of map) { console.log(key + "is" + value); } //first is hello //second is world
If you only want to get the key name, or just want to get the key value, you can write it as follows.
//Get the key name for (let [key] of map) { //... } //Get key value for (let [,value] of map) { //... }
When loading a module, it is often necessary to specify which methods to enter. Destructuring assignment makes the input sentence very clear.
const {SourceMapConsumer, SourceNode} = require("source-map");