我是小又又,住在武汉,做了两年新媒体,准备用 6 个月时间转行前端。
今日学习目标
昨天基于搜索基础学习了解构.所以今天继续学习对象解构,又是适合学习的一天,加油,小又又!!!!
基础用法
基础使用
解构不仅可以用于数组,还可以用于对象。
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;
而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
(function (log) {
const {
bar,
foo
} = {
foo: "aaa",
bar: "bbb"
};
log(foo); // "aaa"
log(bar); // "bbb"
const {
baz
} = {
foo: "aaa",
bar: "bbb"
};
log(baz); // undefined
})(console.log)

上面代码的第一个例子,等号左边的两个变量的次序,与等号右边两个同名属性的次序不一致,但是对取值完全没有影响。
第二个例子的变量没有对应的同名属性,导致取不到值,最后等于undefined。
好像比昨天数组解构厉害一些~~~~
变量名与属性名不一致
如果变量名与属性名不一致,必须写成下面这样。
(function (log) {
const {
foo: baz
} = {
foo: 'aaa',
bar: 'bbb'
};
log(baz); // "aaa"
const obj = {
first: 'hello',
last: 'world'
};
const {
first: f,
last: l
} = obj;
log(f); // 'hello'
log(l); // 'world'
})(console.log)

这实际上说明,对象的解构赋值是下面形式的简写。
(function (log) {
const {
foo: foo,
bar: bar
} = {
foo: "aaa",
bar: "bbb"
};
log(foo); // "aaa"
log(bar); // "bbb"
})(console.log)

也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。
真正被赋值的是后者,而不是前者。
(function (log) {
const {
foo: baz
} = {
foo: "aaa",
bar: "bbb"
};
log(baz); // "aaa"
log(foo); // ReferenceError: foo is not defined
})(console.log)

上面代码中,foo是匹配的模式,baz才是变量。
真正被赋值的是变量baz,而不是模式foo。
原来是这样啊~~~
解构对象方法
对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量。
(function (log) {
const {
log: log1,
sin,
cos
} = Math;
log(log1); // [Function: log]
log(sin); // [Function: sin]
log(cos); // [Function: cos]
})(console.log)

上面代码将Math对象的对数、正弦、余弦三个方法,赋值到对应的变量上,使用起来就会方便很多。
看到
对数、正弦、余弦感觉忘记的差不多了,都还给大学老师了~~~~
属性名表达式
由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。
(function (log) {
const arr = [1, 2, 3];
const {
0: first,
[arr.length - 1]: last
} = arr;
log(first); // 1
log(last); // 3
})(console.log)

上面代码对数组进行对象解构。数组arr的0键对应的值是1,[arr.length - 1]就是2键,对应的值是3。
方括号这种写法,属于属性名表达式。
这个昨天学习数组解构的时候好像也有学习过~~~
基础嵌套对象解构
与数组一样,解构也可以用于嵌套结构的对象。
(function (log) {
const obj = {
p: [
'Hello',
{
y: 'little'
}
]
};
const {
p: [x, {
y
}]
} = obj;
log(x); // "Hello"
log(y); // "little"
})(console.log)

注意,这时p是模式,不是变量,因此不会被赋值。
如果p也要作为变量赋值,可以写成下面这样。
(function (log) {
const obj = {
p: [
'Hello',
{
y: 'little'
}
]
};
const {
p,
p: [x, {
y
}]
} = obj;
log(x); // "Hello"
log(y); // "little"
log(p); // [ 'Hello', { y: 'little' } ]
})(console.log)

所以这个重要的就是要记住,
变量会被赋值,模式不会被赋值.
多重嵌套对象解构
与数组一样,解构也可以用于嵌套结构的对象。
(function (log) {
const node = {
loc: {
start: {
line: 1,
column: 5
}
}
};
const {
loc,
loc: {
start
},
loc: {
start: {
line
}
}
} = node;
log(line); // 1
log(loc); // { start: { line: 1, column: 5 } }
log(start); // { line: 1, column: 5 }
})(console.log)

上面代码有三次解构赋值,分别是对loc、start、line三个属性的解构赋值。
注意,最后一次对line属性的解构赋值之中,只有line是变量,loc和start都是模式,不是变量。
结合数组进行嵌套赋值
(function (log) {
const obj = {};
const arr = [];
({
foo: obj.prop,
bar: arr[0]
} = {
foo: 123,
bar: true
});
log(obj); // { prop: 123 }
log(arr); // [ true ]
})(console.log)

数组和对象解构还可以结合一起用~~~
设置默认值
对象的解构也可以指定默认值。
(function (log) {
const {
x1 = 3
} = {};
log(x1); // 3
const {
x2,
y2 = 5
} = {
x2: 1
};
log(x2); // 1
log(y2); // 5
const {
x3: y3 = 3
} = {};
// log(x3); // ReferenceError: x3 is not defined
log(y3); // 3
const {
x4: y4 = 3
} = {
x: 5
};
// log(x4); // ReferenceError: x4 is not defined
log(y4); // 3
const {
message: msg = 'Something went wrong'
} = {};
log(msg); // "Something went wrong"
})(console.log)

默认值生效的条件是,对象的属性值严格等于undefined。
(function (log) {
const {
x1 = 3
} = {
x1: undefined
};
log(x1); // 3
const {
x2 = 3
} = {
x2: null
};
log(x2); // null
})(console.log)

上面代码中,属性x等于null,因为null与undefined不严格相等,所以是个有效的赋值,导致默认值3不会生效。
注意事项
解构不成功
如果解构不成功,变量的值就等于undefined。
(function (log) {
const {foo} = {bar: 'baz'};
log(foo); // undefined
})(console.log)

父属性不存在
如果解构模式是嵌套的对象,而且子对象所在的父属性不存在,那么将会报错。
(function (log) {
// 报错
const {foo: {bar}} = {baz: 'baz'};// TypeError: Cannot destructure property `bar` of 'undefined' or 'null'.
const _tmp = {baz: 'baz'};
_tmp.foo.bar // Cannot read property 'bar' of undefined
})(console.log)

上面代码中,等号左边对象的foo属性,对应一个子对象。该子对象的bar属性,解构时会报错。
原因很简单,因为foo这时等于undefined,类似于上文的_tmp.foo.bar,再取子属性就会报错。
已经声明的变量
如果要将一个已经声明的变量用于解构赋值,必须非常小心。
(function (log) {
// 错误的写法
const x;
{x} = {x: 1};
// SyntaxError: Unexpected token =
})(console.log)

上面代码的写法会报错,因为 JavaScript 引擎会将{x}理解成一个代码块,从而发生语法错误。
只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。
(function (log) {
// 正确的写法
const x;
({x} = {x: 1});
log(x);// 1
})(console.log)

上面代码将整个解构赋值语句,放在一个圆括号里面,就可以正确执行。
今日学习总结

今日心情
今日主要是基于搜索来基础学习对象解构,这个希望明天学到更多的内容~~~~

本文使用 mdnice 排版