May 08, 2021 ES6
1. 1. Deconstructed assignment of the array
3. 2. The deconstruction assignment of the object
6. 3. Deconstructed assignment of strings
7. 4. Deconstructed assignments for values and boolean values
8. 5. Deconstructed assignment of function parameters
Basic usage
ES6 allows variables to be assigned values from arrays and objects in a certain
解构
known as deconstruction.
Previously, variables were assigned values that could only be specified directly.
let a = 1;
let b = 2;
let c = 3;
ES6 allows this to be written below.
let [a, b, c] = [1, 2, 3];
The above code indicates that values can be extracted from the array and assigned to variables according to their corresponding locations.
Essentially, this writing is
“模式匹配”
which the variable on the left is given a corresponding value as long as the patterns on both sides of the equal sign are the same.
Here are some examples of deconstructing using nested arrays.
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
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 deconstruction is unsuccessful, the value of the variable
undefined
Another scenario is incomplete
不完全解构
i.e. the pattern to the left of the equal sign matches only a portion of the array to the right of the equal sign.
In this case, deconstruction can still succeed.
let [x, y] = [1, 2, 3];
x // 1
y // 2
let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4
Both of the above examples are incomplete deconstructions, but they work.
If the right side of the equal sign is not an array (or, strictly speaking, not a traversable structure, see chapter Iterator), then an error will be reported.
// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
The above statements are correct because the value to the right of the equal sign either does not have the Iterator interface (the first five expressions) after the object is converted, or it does not have the Iterator interface itself (the last expression).
For
Set
you can also use array deconstruction assignments.
let [x, y, z] = new Set(['a', 'b', 'c']);
x // "a"
In fact, as long as a data structure has
Iterator
it can be deconstructed as 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 code above, fibs is a Generator function (see chapter "Generator Functions"), native to the Iterator interface. Deconstructing assignments gets values from this interface in turn.
解构赋值
allows you to
默认值
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 a strict equal operator internally to determine whether a location has a value. Therefore, the default value takes effect only if an array member is strictly equal to underfined.
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 does not take effect because null is not strictly equal to undefined.
If the default value is an expression, the expression is lazy, i.e. it is evaluated only when it is used.
function f() {
console.log('aaa');
}
let [x = f()] = [1];
In the above code, function f is not executed at all because x can get a value. 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 that deconstruct assignments, 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 reported an error because y had not been declared when x used y as the default.
解构
can be used
数组
but also for
对象
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
The deconstruction of an object is an important difference from an array.
The elements of the array are arranged in
变量
the value of the variable is determined by its position, and the properties of the object are not in order, and the variable must have
属性
same name as the property in order to get the correct value.
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined
The first example of the code above, the order of the two variables to the left of the equal sign, is inconsistent with the order of the two properties of the same name to the right of the equal sign, but has no effect on the value at all. The variable in the second example does not have a corresponding property of the same name, resulting in a value that is not obtained and is eventually equal to undefined.
If deconstruction fails, the value of the variable is equal to the undefined.
let {foo} = {bar: 'baz'};
foo // undefined
In the code above, the object to the right of the equal sign does not have a foo property, so the variable foo does not get a value, so it is equal to undefined.
The deconstruction assignment of an object makes it easy to assign methods of an existing object to a variable.
// 例一
let { log, sin, cos } = Math;
// 例二
const { log } = console;
log('hello') // hello
The example of the above code assigns three methods of the Math object to the corresponding variable, which will be much easier to use. Example 2 assigns the .log to the log variable.
If the variable name does not match the property name, it must be written as below.
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 deconstructed assignment of an object is a short case in the form below (see chapter Extensions of Objects).
let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' };
That is, the internal mechanism for deconstructing an assignment of an object is to find the property of the same name before assigning it to the corresponding variable. It is the latter, not the former, that is really assigned.
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // error: foo is not defined
In the code above, foo is the pattern of the match, and baz is the variable. It is the variable baz, not the pattern foo, that is really assigned.
Like arrays, deconstruction 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 p is a pattern, not a variable, and is therefore not assigned. If p is also assigned as a variable, it can be written as below.
let obj = {
p: [
'Hello',
{ y: 'World' }
]
};
let { p, p: [x, { y }] } = obj;
x // "Hello"
y // "World"
p // ["Hello", {y: "World"}]
Here's 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 deconstructs the assignment three times, namely, the deconstruction assignment of the loc, start, and line properties. Note that the last deconstruction assignment of line properties, only line is a variable, loc and start are patterns, not variables.
The following is an example of nested assignments.
let obj = {};
let arr = [];
({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true });
obj // {prop:123}
arr // [true]
If the deconstruction pattern is a nested object and the parent property in which the child object is located does not exist, an error will be reported.
// 报错
let {foo: {bar}} = {baz: 'baz'};
In the code above, the foo property of the object to the left of the equal sign corresponds to a sub-object. T he bar property of the sub-object, which is deconstructed with errors. The reason is simple, because foo is equal to undefined at this time, and then take sub-attributes will report errors.
Note that the deconstruction assignment of an object can be taken from inherited properties.
const obj1 = {};
const obj2 = { foo: 'bar' };
Object.setPrototypeOf(obj1, obj2);
const { foo } = obj1;
foo // "bar"
In the code above, the prototype object of object obj1 is obj2. The foo property is not a property of obj1 itself, but a property inherited from obj2, which can be taken from deconstructing assignments.
对象
解构
object can also
默认值
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 default value takes effect on the condition that the
属性值
object is
undefined
var {x = 3} = {x: undefined};
x // 3
var {x = 3} = {x: null};
x // null
In the above code, the property x is equal to null, because null is not strictly equal to undefined, so it is a valid assignment, resulting in the default value 3 will not take effect.
(1) If you want to use a declared variable for deconstructing assignments, you must be very careful.
// 错误的写法
let x;
{x} = {x: 1};
// SyntaxError: syntax error
The above code is written incorrectly because the JavaScript engine will understand the code block as a block of code, resulting in a syntax error. You can solve this problem only if you don't write braces at the top of the line and avoid JavaScript interpreting them as blocks of code.
// 正确的写法
let x;
({x} = {x: 1});
The above code deconstructs the assignment statement and places it in a parenthesis so that it can be executed correctly. See below for the relationship between parentheses and deconstructed assignments.
(2) Deconstructing assignments allows no variable names to be placed in the mode to the left of the equal sign. Therefore, you can write out very odd assignment expressions.
({} = [true, false]);
({} = 'abc');
({} = []);
The above expression is meaningless, but the syntax is legal and can be executed.
(3) Because arrays are special objects in nature, they can be deconstructed by object properties.
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
The above code deconstructs the array. T he 0 key of the array arr corresponds to the value of 1, and the value of the array arr.length - 1 is 2 keys, and the corresponding value is 3. Square brackets, which are written in the form of "attribute name expressions" (see chapter extended to objects).
Strings can also deconstruct assignments. This is because at this point, 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"
Array-like objects have a
length
so you can deconstruct the assignment of that property.
let {length : len} = 'hello';
len // 5
When deconstructing an assignment, if the equal
右边
is a value and a Boolean value, it is first converted to the
对象
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
In the code above, both the value and the wrapper object of the
toString
so
s
can get the value.
The rule for deconstructing assignments is to convert an equal sign to an object or array as long as the value to the right is not an object or array.
Because
undefined
null
be converted to objects, deconstructing assignments on them will result in errors.
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
函数
of
参数
can also be
解构赋值
function add([x, y]){
return x + y;
}
add([1, 2]); // 3
In the above code, the
add
of the function add are on the surface of an array, but at the moment the arguments are passed in, the array parameters are
x
y
For code inside functions, the parameters they can feel are
x
and
y
Here's another example.
[[1, 2], [3, 4]].map(([a, b]) => a + b);
// [ 3, 7 ]
函数参数
解构
can also use
默认值
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, the
move
the function move is an object that is deconstructed to get the
x
and
y
If deconstruction fails,
x
and
y
equal to the default values.
Note that the following writing will get a different result.
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 specifies a default value for the parameters of the function
move
not
x
and
y
so you get a different result than the previous one.
undefined
the default value of the function argument.
[1, undefined, 3].map((x = 'yes') => x);
// [ 1, 'yes', 3 ]
Deconstructing assignments is convenient, but it's not easy to parse. For the compiler, whether a pattern is a pattern or an expression is not known from the start, and must be parsed to (or not resolved) equivalents to know.
The question arises as to what to do if parentheses appear in the pattern. ES6's rule is that parentheses should not be used whenever there is ambiguity that could lead to deconstruction.
However, this rule is actually less easily identifiable and quite cumbersome to deal with. Therefore, it is recommended that you do not place parentheses in the pattern whenever possible.
The following three deconstruction assignments must not use parentheses.
(1) Variable declaration statement
// 全部报错
let [(a)] = [1];
let {x: (c)} = {};
let ({x: c}) = {};
let {(x: c)} = {};
let {(x): c} = {};
let { o: ({ p: p }) } = { o: { p: 2 } };
The above six statements are error-positive because they are all variable declaration statements and patterns cannot use parentheses.
(2) Function parameters
函数参数
also belong
变量声明
and therefore cannot be parenthesed.
// 报错
function f([(z)]) { return z; }
// 报错
function f([z,(x)]) { return x; }
(3) The pattern of the assignment statement
// 全部报错
({ p: a }) = { p: 42 };
([a]) = [5];
The code above places the entire pattern in parentheses, resulting in an error.
// 报错
[({ p: a }), { x: c }] = [{}, {}];
The code above places a portion of the pattern in parentheses, resulting in an error.
There is only one case in which you can use parentheses: the non-patterned part of the assignment statement, and you can use parentheses.
[(b)] = [3]; // 正确
({ p: (d) } = {}); // 正确
[(parseInt.prop)] = [3]; // 正确
The above three lines of statements can be executed correctly because first they are assignment statements, not declaration statements;
In the first line statement, the pattern is the first member of the array,
p
d
of parentheses;
The deconstruction assignment of variables has many uses.
(1) Exchange the value of the variable
let x = 1;
let y = 2;
[x, y] = [y, x];
The above code exchanges
x
of the variables x
y
which are not only concise, but also easy to read, with very clear semantics.
(2) Multiple values are returned from the function
The function can only return one value, and if you want to return more than one value, you can only return them in an array or object. With an understanding of the assignment, it is very convenient to remove these values.
// 返回一个数组
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
(3) Definition of function parameters
Deconstructing assignments makes it easy to match a set of parameters to variable names.
// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);
// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});
(4) Extract JSON data
解构赋值
is especially useful for extracting data from
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]
The above code quickly extracts the value of the JSON data.
(5) The default value of the function argument
jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// ... more config
} = {}) {
// ... do stuff
};
By specifying the default value of the argument, it is possible to write
var foo = config.foo || 'default foo';
Such a statement.
(6) Traversing the Map structure
Any object with an Iterator interface deployed can use
for...of
traversal.
The Map structure natively supports the Iterator interface, which makes it easy to get key names and key values with the deconstruction 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 just want to get the key name, or just want to get the key value, you can write it like this.
// 获取键名
for (let [key] of map) {
// ...
}
// 获取键值
for (let [,value] of map) {
// ...
}
(7) Enter the specified method of the module
When you load a module, you often need to specify which methods to enter. Deconstructing assignments makes the input statement very clear.
const { SourceMapConsumer, SourceNode } = require("source-map");