ES6-Destructuring (Destructuring)

ES6-Destructuring (Destructuring)

Destructuring and Assignment of Array

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 fibsis a Generator function, which has an Iterator interface natively. Destructuring assignment will in turn obtain values ​​from this interface.

Defaults

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 undefineddefault 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 nullnot 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 xthe value can be retrieved, the function fwill 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 xused yas a default value y.

Object destructuring assignment

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 Maththree 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, foothe matched pattern bazis the variable. It is the variable bazthat 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 pis a mode, not a variable, so it will not be assigned. If it is palso 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, linedeconstruction three attributes assignment. Note that in the last linedeconstruction assignment of attributes, only linevariables were variables, locand startboth 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 fooattribute of the object on the left side of the equal sign corresponds to a child object. The barproperties of this sub-object will report an error during deconstruction. The reason is simple, because at foothis 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, obj1the prototype object of the object is obj2. fooThe attribute is not its obj1own attribute, but the attribute inherited from it obj2, which can be obtained by destructuring assignment.

Default value of object destructuring

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 xis equal null, because it nullis undefinednot strictly equal, so it is a valid assignment, causing the default value to 3not take effect.

be careful

(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 arrof 0keys corresponds to a value 1, [arr.length - 1]is 2the key, the corresponding value 3. Square brackets are "attribute name expressions"

Destructuring and Assignment of String

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 lengthattribute, so you can also deconstruct and assign values ​​to this attribute.

let {length: len} ='hello';
len//5

Destructuring and Assignment of Numerical and Boolean Values

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 toStringattributes, so variables scan 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 undefinedsums nullcannot be converted into objects, destructuring and assigning them will cause an error.

let {prop: x} = undefined;//TypeError
let {prop: y} = null;//TypeError

Destructuring assignment of function parameters

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, addthe 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 xsum y. For the code inside the function, the parameters they can feel are xsum 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, movethe parameter of the function is an object, xand ythe value of the variable sum is obtained by deconstructing this object . If the deconstruction fails, the xsum yequals 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 movespecify default values for the parameters of the function , not for variables xand yspecify default values, so you will get a different result from the previous way of writing.

undefinedThe default value of the function parameter will be triggered.

[1, undefined, 3].map((x ='yes') => x);
//[1,'yes', 3]

Parenthesis problem

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.

Cases where parentheses cannot be used

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.

Cases where parentheses can be used

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.

Deconstruction use

1. Exchange the value of a variable

let x = 1;
let y = 2;

[x, y] = [y, x];

The above code exchanges the values ​​of variables xand yvalues, which is not only concise, but also easy to read, with very clear semantics.

2. Return multiple values ​​from a function

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();

3 Definition of function parameters

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});

4 Extract JSON data

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]

5 Default values ​​of function parameters

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 .

6 Traverse the Map structure

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) {
 //...
}

7 How to specify the input module

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");
Reference: https://cloud.tencent.com/developer/article/1464399 ES6-Destructuring-Cloud + Community-Tencent Cloud