-
Notifications
You must be signed in to change notification settings - Fork 0
/
Destructuring.html
327 lines (264 loc) · 11.3 KB
/
Destructuring.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Destructuring</title>
</head>
<body>
<script>
// ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构
/*var [a, b, c] = [1, 2, 3];
console.log(a);
console.log(b);
console.log(c);*/
/*let [foo, [[bar], baz]] = [1, [[2], 3]]
console.log(foo, bar, baz);
let [ , ,test] = ['1', '2', '3']
console.log(test);
let [head, ...tail] = [1,2,3,4]
console.log(head);
console.log(tail);
// 如果解构不成功,变量的值就等于undefined
let [x, y, z] = ['a']
console.log(x);
console.log(y);
console.log(z);*/
// 另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。
/*let [x, y] = [1,2,3];
console.log(x);
console.log(y);
let [a, [b], c] = [1, [2,3], 4];
console.log(a);
console.log(b);
console.log(c);*/
// 如果等号的右边不是数组(或者严格地说,不是可遍历的结构),那么将会报错。
/*let [foo] = 1;
console.log(foo); // 报错*/
// 只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值。
// let,const,Set
// 下面代码中fibs是一个Generator函数,原生具有Iterator接口。解构赋值会依次从这个接口获取值。
/*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();
sixth // 5*/
// 解构赋值允许制定默认值
/*let [foo = true] = [];
console.log(foo);
[x, y = 'b'] = ['a'];
console.log(x);
console.log(y);
[a, b = 'y'] = ['x', undefined]
console.log(a);
console.log(b);*/
// ES6内部使用严格相等运算符(===),判断一个位置是否有值。所以,如果一个数组成员不严格等于undefined,默认值是不会生效的。
/*var [x = 1] = [undefined];
var [y = 1] = [null];
console.log(x);
console.log(y);*/
// 如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。
/*function foobar() {
return 5;
}
let [x = foobar()] = [1];
console.log(x);*/
// 默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
/*let [x = 1, y = x] = [];
console.log(x,y);
let [a = 1, b = a] = [2];
console.log(a,b);
let [o = 1, p = o] = [1,2];
console.log(o,p);
let [s = t, t = 1] = [];
console.log(s, t); // 报错*/
// 对象的解构与数组有一个重要的不同。
// 数组的元素是按次序排列的,变量的取值由它的位置决定;
// 而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
/*var {foo, bar} = {foo:'123', bar: '456'};
console.log(foo, bar);
// 如果变量名与属性名不一致,必须写成下面这样
var {foo: baz} = {foo: '123', bar: '4567'}
console.log(baz);*/
// 这实际上说明,对象的解构赋值是下面形式的简写
// var { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };
// 对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
// 解构也可以用于嵌套结构的对象
/*var node = {
loc: {
start: {
line: 1,
column: 5
}
}
};
var { loc: { start: { line }} } = node;
// 上面代码中,只有line是变量,loc和start都是模式,不会被赋值。*/
// 对象的解构也可以指定默认值。
// 默认值生效的条件是,对象的属性值严格等于undefined。
/*let {x = 3} = {};
console.log(x);
var {a, b = 5} = {a: 1};
console.log(a,b);
var {s:t = 5} = {};
console.log(t);
var {m:n = 5} = {m: 7};
console.log(n);
var {o = 3} = {o: undefined}
console.log(o);*/
// 如果解构失败,变量的值等于undefined。
/*let {foo} = {bar: 'baz'}
console.log(foo);*/
// 如果解构模式是嵌套的对象,而且子对象所在的父属性不存在,那么将会报错。
/*var {foo: {baz}} = {bar: 'bar'};
console.log(baz); // 报错*/
// 如果要将一个已经声明的变量用于解构赋值,必须非常小心。
/*var x;
{x} = {x:1}
console.log(x);*/
// 上面代码的写法会报错,因为JavaScript引擎会将{x}理解成一个代码块,从而发生语法错误。
// 只有不将大括号写在行首,避免JavaScript将其解释为代码块,才能解决这个问题。
/*var x;
({x} = {x: 1})
console.log(x);*/
// 解构赋值允许,等号左边的模式之中,不放置任何变量名。因此,可以写出非常古怪的赋值表达式。
// 表达式虽然毫无意义,但是语法是合法的,可以执行。
/*({} = [true, false]);*/
// 对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量。
// 将Math对象的对数、正弦、余弦三个方法,赋值到对应的变量上,使用起来就会方便很多。
/*let {log, sin, cos} = Math;
console.log(log(16));*/
// 由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。
/*var arr = [1,2,3];
var {'0': first, [arr.length-1]: last} = arr;
console.log(first);
console.log(last);*/
// 字符串也可以解构赋值
// 这是因为此时,字符串被转换成了一个类似数组的对象。
/*const [a, b, c, d, e] = 'hello';
console.log(a,b,c,d,e);*/
// 类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。
/*let {length: len} = 'hello'
console.log(len);*/
// 解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。
// 数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。
/*let {toString: s} = 123;
console.log(s === Number.prototype.toString);
let {toString: x} = true;
console.log(x === Boolean.prototype.toString);*/
// 解构赋值的规则是,只要等号右边的值不是对象,就先将其转为对象。
// 由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。
/*let {prop: x} = undefined;
let {prop: y} = null;
console.log(x,y);*/
// 函数参数的解构赋值
/*function add([x,y]) {
return x + y;
}
console.log(add([1,2]));
let x = [[1,2], [3,4]].map(([a,b]) => a+b);
console.log(x);
// 函数参数的解构赋值也可以使用默认值
function move({x = 0, y=0} = {}) {
return [x,y];
}
console.log(move({x:3,y:8}));
console.log(move());
// 同样,undefined就会触发默认值
console.log([1, undefined, 3].map((x = 'yes') => x));*/
// 对于编译器来说,一个式子到底是模式,还是表达式,没有办法从一开始就知道,必须解析到(或解析不到)等号才能知道。
// 由此带来的问题是,如果模式中出现圆括号怎么处理。ES6的规则是,只要有可能导致解构的歧义,就不得使用圆括号。
// 建议只要有可能,就不要在模式中放置圆括号。
// 以下三种情况不能使用圆括号
// 1. 变量声明语句中
/*var [(a)] = [1];
var {x: (c)} = {};*/
// 2. 函数参数
/*function f([(z)]) {
return z
}*/
// 3. 赋值语句中,不能将整个模式,或嵌套模式中的一层,放在圆括号之中。
/*([a]) = [5]*/
// 可以使用圆括号的情况只有一种:赋值语句的非模式部分,可以使用圆括号。
/*[(a)] = [2]
console.log(a);*/
// 用途
// 1. 交换变量的值
/*let x = 5;
let y = 19;
[x,y] = [y,x]
console.log(x, y);*/
// 2. 函数返回多个值
// 函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。
// 有了解构赋值,取出这些值就非常方便。
/*function example() {
return [1,2,3];
}
var [x,y,z] = example();
console.log(x,y,z);
function example1() {
return {
foo: '1',
bar: 2
}
}
let {foo:a, bar:b} = example1();
console.log(a, b);*/
// 3. 函数参数的定义
// 解构赋值可以方便地将一组参数与变量名对应起来。
/*function foo([x,y,z]) {
console.log(x,y,z);
}
foo([1,2,3]);
function bar({x,y,z}) {
console.log(x,y,z);
}
bar({y:1,x:5,z:10});*/
// 4. 提取JSON数据
/*var jsonData = {
id: 42,
name: 'Kate',
data: [866, 665]
};
let {id, name, data} = jsonData;
console.log(id, name, data);*/
// 5. 函数参数的默认值
// 指定参数的默认值,就避免了在函数体内部再写var foo = config.foo || 'default foo';这样的语句。
/*jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// ... more config
}) {
// ... do stuff
};*/
// 6. 遍历Map结构
// 任何部署了Iterator接口的对象,都可以用for...of循环遍历。
// Map结构原生支持Iterator接口,配合变量的解构赋值,获取键名和键值就非常方便。
/*var map = new Map();
map.set('first', 'Hello');
map.set('second', 'World');
for (let [key,value] of map) {
console.log(key + ':' + value);
}
// 只获得键名
for (let [key] of map) {
console.log('key is: ' + key);
}
// 只获得值
for (let [,value] of map) {
console.log('value is: ' + value);
}*/
// 7. 输入模块的指定方法
// 加载模块时,往往需要指定输入那些方法。解构赋值使得输入语句非常清晰。
/*const { SourceMapConsumer, SourceNode } = require("source-map");*/
</script>
</body>
</html>