【JS学习笔记】对象-解构

386 阅读5分钟

这是我参与更文挑战的第8天,活动详情查看: 更文挑战

对象解构

ES6新增,同时执行多个赋值操作。MDN

将对象的某个属性提取到某个变量中。

基本用法

const herb = {
    name: "herb",
    age: 18
}
let herbName = herb.name,
    herbAge = herb.age;

以前的方法过于繁琐。

let { name: herbName, age: herbAge } = herb;

现在通过这种方式,两段代码等效。

let { 属性名:变量名 } = 解构对象

let { name, age } = herb;
// 相当于:let name = herb.name; let age = herb.age;

=右边放要解构的对象,可以是返回一个对象的表达式

=左边的{}括号里面,表示要把哪些属性名从对象那里解构出来,:后面跟着变量名,就是说,属性名对应的属性值存放的地方。如果没有:则表示属性名和变量名相同。

let name, age;
( { name, age } = herb );

如果给事先声明的变量赋值,则赋值表达式必须包含在一对括号中。

解构赋值的同时定义默认值

const herb = {
    name: "herb",
    age: null
}
let { name: herbName = "person", age = 20, sex = "男" } = herb;

// name: "herb"
// age: undefined
// sex: "男"

let { 属性名:变量名 = 默认值 } = 解构对象

herb.sex不存在时,会使用默认值,或者对象中的属性值为undefiend,也会用默认值。

上述代码,相当于:先定义了herbNameagesex三个变量,再从对象中:前面的属性名读取出来赋值给这几个变量,如果属性值为undefined,就使用默认值。

可以解构原型链上的属性

class Animal {
    constructor(type, age, sex) {
        this.type = type;
        this.age = age;
        this.sex = sex;
    }
    call() {
        console.log("aaaa");
    }
}
class Person extends Animal {
    constructor(type, age, sex, name) {
        super(type, age, sex);
        this.name = name;
    }
}
const herb = new Person("人类", 18, "男", "herb");
let { name, age, sex, call } = herb;
// name: "herb"
// age: 18
// sex: "男"
// call: call() { console.log("aaaa") }

可以把原型链上的属性也解构出来,无论是否可以遍历。

因为只要在原型链上就可以被对象访问到,所以也可以赋值给新的变量。

使用计算属性名

const arr = [1,2,3,4];
let { [Symbol.iterator]:it } = arr;
// it: function
let key = "z";
let { [key]: foo } = { z: "bar" };
// foo: "bar"

可以结合使用。

隐式把待解构目标转换成对象

let { length } = "1234";
// length: 4
let { constructor:c } = 100;
// c == Number

原始值会被转换成对象在进行解构。

let { a } = null
let { b } = undefined

nullundefined不能被解构,否则会报错。

嵌套解构

const user = {
    name: "herb",
    age: 19,
    sex: "男",
    address: {
        province: "浙江",
        city: "嘉兴"
    }
}
let { address } = user;
// address
// {
//       province: "浙江",
//       city: "嘉兴"
// }

解构出来是一个对象。

let { address: { province, city } } = user;
// address 不存在
// province: "浙江"
// city: "嘉兴"

let创建了provincecity两个变量,不会创建address,只是从user里找到address,并从address里找到provincecity

let { province, city } = user.address;

也可以这样写,直接在=右边把要结构的对象找出来,不用在{}里面嵌套解构了。

let { hobby: { food, sport } } = user;
// Uncaught TypeError: Cannot read property 'food' of undefined

注意:外层属性没有定义的情况下不能使用嵌套结构。user没有hobby属性,对其进行嵌套解构肯定会出错。

数组解构

const arr =  [0, 1, 2, 3, 4];
const { 0: a, 4: e } = arr;
// a: 0
// e: 4

数组本质上就是一个对象,所以可以通过对象的方式解构。

const [a, , , , e] = arr;
// a: 0
// e: 4

数组也有自己的解构方式,用[]表示,里面变量的顺序代表了arr里面元素的顺序。省略了索引,是上一个写法的简化版本,但是要获取不连续的两个索引,要用,分隔,表示跳过数组里多少元素。

const arr =  [0, 1, [2, 3, 4]];
let [, , [c, d]] = arr;
// c: 2
// d: 3
let [two, three] = arr[2];
// two: 2
// three: 3

也可以嵌套解构。

剩余参数

const user = {
    name: "herb",
    age: 19,
    sex: "男",
    address: {
        province: "浙江",
        city: "嘉兴"
    }
}
let { name, ...prop } = user;
// name: "herb"
// prop: {
//   address: { province: "浙江", city: "嘉兴" }
//   age: 19
//   sex: "男"
// }

...prop剩余参数会把没有解构的属性全部收集起来,封装成新的对象。

可以很方便的把一个对象的某些属性剔除出来。

let {...prop1, age, ...prop2 } = user;
// Uncaught SyntaxError: Rest element must be last element

注意:剩余参数只能出现在最后一位。

交换变量

let a = 1;
let b = 3;

[a, b] = [b, a];

ab组装成一个数组,再通过解构交换变量。

a = a ^ b;
b = a ^ b;
a = a ^ b;

也可以这样交换位置,但仅限于number类型。

复杂解构

const article = {
    title: "文章标题",
    content: "文章内容",
    comments: [{
        content: "评论1",
        user: {
            id: 1,
            name: "用户名1"
        }
    }, {
        content: "评论2",
        user: {
            id: 2,
            name: "用户名2"
        }
    }]
}
// 解构出第二条评论的用户名和评论内容
// 方法一
const {
    comments: [, {
        content,
        user: {
            name
        }
    }]
} = article;
// 方法二
const {
    content,
    user: {
        name
    }
} = article.comments[1]

可以灵活运用。

For of 迭代和解构

for(let { user: { name }, content } of article.comments) {
    console.log(name, content);
}

利用上面的例子,通过for-of可以轻松的把数组中每一个对象的具体属性都解构出来。

由于for-of只能迭代那种实现了Iterable接口的数据类型,所以无法用于普通对象上。

const obj = {
    a: 1,
    b: 2
}
for(let prop of obj) {
    console.log(prop);
}
// Uncaught TypeError: obj is not iterable

参数解构

function print({ name, age, sex, address: { province, city } }) {
    console.log(name, age, sex, province, city);    
}

print({
    name: "herb",
    age: 19,
    sex: "男",
    address: {
        province: "浙江",
        city: "嘉兴"
    }
});

可以直接在形参上进行解构,省去了在函数体力反复调用对象获取值的过程。并且不会对arguments产生影响,它依然保存着传进来的对象。

function ajax({
    method = "get",
    url = "/"
} = {}) {
    console.log(method, url);
}
ajax({
    method: "get",
    url: "www.juejin.cn"
})

在解构中使用默认值,考虑到了传进来的对象没有这个属性的情况。给形参设置默认值,防止不传对象进来,对undefined进行解构的情况。代码变得简洁而健壮。