Neusofts

科技改变生活,创新引领未来!

ES6/ES2015解构及扩展运算符

Javascript ES6/ES2015,其中许多特性其实是为了简化代码。解构运算符扩展运算符rest运算符就是其中很好的特性,它们可以通过减少赋值语句的使用,或者减少通过下标访问数组或对象的方式,使代码更加简洁优雅,可读性更佳。

 

解构

解构的作用是可以快速取得数组或对象当中的元素或属性,而无需使用arr[index]或者obj[key]等传统方式进行赋值

数组解构赋值:

let arr = ['字符串', 11, true];

// 传统方式:
var a = arr[0], b = arr[1], c = arr[2];

// 解构赋值,效果相同
let [a, b, c] = arr;

 

嵌套数组解构:

let arr = [['字符串', 11, true], [1, 2, 3], [4, 5, 6]];

let [[a, b, c], d, e] = arr;
// 结果:a == '字符串',b == 11,c == true,d == [1, 2, 3], e == [4, 5, 6]

 

函数传参解构:

let arr = ['字符串', 11, true];

;(function ([a, b, c]) {
    // 结果:a == '字符串',b == 11,c == true
})(arr);

 

for循环解构:

let arr = [ ['字符串', 11, true], [1, 2, 3], [4, 5, 6] ];

for (let [a, b, c] of arr) {
    // 字符串 11 true
    // 1 2 3
    // 4 5 6
}

 

对象赋值解构:

let obj = {
    name: '名字',
    age: 22,
    eat: {
        food1: '苹果',
        food2: '香蕉'
    }
};

let {name, age, eat} = obj;
// 结果:name == '名字',age == 22, eat == [object Object]

let {food1, food2} = eat;
// 结果:food1 == '苹果',food2 == '香蕉'

 

对象传参解构:

let obj = {
    name: '名字',
    age: 22,
    eat: {
        food1: '苹果',
        food2: '香蕉'
    }
};

;(function ({name, age, eat}) {
    // 结果:name == '名字',age == 22, eat == [object Object]
})(obj);

 

变量名与对象属性名不一致解构(指定别名):

let obj = {
    name: '名字',
    age: 22
};

let {name: n, age: a} = obj;
// 结果:n == '名字',a == 22

 

对象嵌套解构:

let obj = {
    name: '名字',
    age: 22,
    eat: {
        food1: '苹果',
        food2: '香蕉'
    }
};

let { name, age, eat: {food1, food2} } = obj;
// 结果:name == '名字',age == 22, food1 = '苹果',food2 == '香蕉'(附注:age不存在)

 

对象嵌套重名解构:

let obj = {
    name: '名字1',
    age: 22,
    friend: {
        name: '名字2',
        age: 20
    }
};

let { name, age, friend: {name: n, age: a} } = obj;
// 结果:name == '名字1',age == 22, n == '名字2',a == 20(附注:friend不存在)

 

循环解构对象:

let arr = [ {name: 'zs', age: 20}, {name: 'ls', age: 21}, {name: 'ww', age: 22} ];
for (let {name, age} of arr) {
    // zs 20
    // ls 21
    // ww 22
}

 

解构特殊场景:

// 字符串处理
let str = 'abc';
let [a, b, c, d] = str;
// 结果:a == 'a', b == 'b', c == 'c', d == 'undefined'

// 值互换
let x = 1, y = 2;
[x, y] = [y, x]; // 不能let定义,因为已存在
// 结果 x == 2, y == 1;

//对任意深度的嵌套数组进行解构
var [foo, [ [bar], baz] ] = [1, [ [2], 3] ];
console.log(foo); // 1
console.log(bar); // 2
console.log(baz); // 3

// 忽略尾随元素
let [first] = [1, 2, 3, 4];
console.log(first); // 1

//忽略部分元素
let [, second, , forth] = [1, 2, 3, 4];
console.log(second); // 2
console.log(forth); // 4

// 没有声明的赋值
({a, b} = {a:'foo', b:12, c:'bar'}); // 注意此处,需要用括号括起来,因为javascript通常会以{起始的语句作为一个代码块。

// 对象展开(属性值覆盖)
let default = {food:'spicy', price:'$', drink:'coko'};
let search = {...default, food:'rich'};
console.log(search); // {food:'rich', price:'$', drink:'coko'}

// 声明给出默认值
let obj = {a: 1, b: 2, c: 3};
let {a, b, c, d = 4} = obj; // a,b,c,d分别等于1,2,3,4

// 对象展开(附注:typescript语法)它只包含自身的可枚举的属性。 并且会丢失展开对象的方法:
class C = {p:12, m(){}};
let c = new C();
let clone = {...c};
console.log(clone.p); // 12
console.log(clone.m); // error!

// 指定类型(附注:typescript语法)
let {a, b} : {a:string, b:number} = o;

// 指定默认值(附注:typescript语法) (即使b为undefined,obj的属性a, b也都会有值)
function keepWhole(obj: { a: string, b?: number }) {
    let { a, b = 1001 } = obj;
};

 

拓展运算符

扩展运算符用三个点号 ... 表示,功能是把数组或类数组对象展开成一系列用逗号隔开的值

 

var arr = [1, 2, 3];
var fn = function (a, b, c) {
    // 结果:a == 1, b == 2, c == 3;
}

// 传统方式:
fn(arr[0], arr[1], arr[2]);

// 拓展运算符方式
fn(...arr);

 

特殊应用场景:

var arr = [1, 2, 3];

// 数组深度拷贝
var arr2 = arr; // arr2 === arr,指向同一引用
var arr3 = [...arr]; // arr3 !== arr,非指向同一引用

// 数组插入
var arr4 = [...arr, 4, 5, 6]; // 结果:[1, 2, 3, 4, 5, 6]

// 字符串转数组
var str = 'abcd';
var arr5 = [...str]; // 结果:['a', 'b', 'c', 'd']

 

rest运算符

rest运算符也是三个点号,不过其功能与扩展运算符恰好相反,把逗号隔开的值序列组合成一个数组

//主要用于不定参数,所以ES6开始可以不再使用arguments对象

;(function (...args) {
    for (let el of args) {
        // 输出:1 2 3 4
    }
})(1, 2, 3, 4);

;(function (a, ...args) {
    // a === 1
    // args == [2, 3, 4]
})(1, 2, 3, 4);

 

rest运算符配合解构使用:

let [a, ...args] = [1, 2, 3, 4];
// a === 1
// args == [2, 3, 4]

 

小结:

=表达式是典型的赋值形式,函数传参和for循环的变量都是特殊形式的赋值。解构的原理是赋值的两边具有相同的结构,就可以正确取出数组或对象里面的元素或属性值,省略了使用下标逐个赋值的麻烦。

对于三个点号...,放在形参或者等号左边rest运算符; 放在实参或者等号右边spread运算符(扩展运算符),或者说,放在被赋值一方为rest运算符,放在赋值一方为扩展运算符。

总结:

1、在等号赋值或for循环中,如果需要从数组或对象中取值,尽量使用解构。
2、在自定义函数时,如果调用者传来的是数组或对象,形参(定义)尽量使用解构方式,优先使用对象解构,其次是数组解构。代码可读性会很好。
3、在调用第三方函数的时候,如果该函数接受多个参数,并且你要传入的实参(调用)为数组,则使用扩展运算符。可以避免使用下标形式传入参数。也可以避免很多人习惯的使用apply方法传入数组。
4、rest运算符使用场景应该稍少一些,主要是处理不定数量参数,可以避免arguments对象的使用。

———— End ————