FrankKai / FrankKai.github.io

FE blog
https://frankkai.github.io/
363 stars 39 forks source link

es6之rest parameters和spread syntax #174

Open FrankKai opened 4 years ago

FrankKai commented 4 years ago

在学习nodeschool的count-to-6教程时,遇到了这两个常用但是一直没有系统学习的概念,因此在mdn找到相关文档进行学习。

FrankKai commented 4 years ago

rest parameters

rest parameters语法

rest parameter语法允许我们将不确定的arguments当作数组。

function sum(...theArgs) {
    return theArgs.reduce((acc, cur)=> acc+cur))
}
console.log(sum(1, 2, 3)); // 6

语法:function f(a, b, ...theArgs){ //... }

函数的最后一个形参可以使用...前缀把剩余的arguments存储在一个标准的js数组中。 只有最后一个参数是“rest parameter”。

rest parameters与arguments的区别

rest parameters没出现前,es5怎么做?

在rest parameters出现之前,可以使用以下几种方式将arguments转化为数组。

function f(a, b) {
    let normalArray = Array.prototype.slice.call(arguments)
    // --or--
    let normalArray = [].slice.call(arguments)
    // --or--
    let normalArray = Array.from(arguments)
}

rest parameters既然是数组,可以直接解构吗?

当然可以。

function f(...[a, b, c]) {
     return a + b + c;
}

f(1) // NaN (b and c are undefined) f(1, 2, 3) // 6 f(1, 2, 3, 4) // 6 (the fourth parameter is not destructured)

优雅的使用rest parameters的例子

function multiply(multiplier, ...theArgs) {
  return theArgs.map(function(element) {
    return multiplier * element
  })
}

let arr = multiply(2, 1, 2, 3)
console.log(arr)  // [2, 4, 6]
// 自我思考:用好rest parameters,避免Array.prototype.forEach到底。
FrankKai commented 4 years ago

Spread syntax

几种spread场景

console.log(sum(...numbers)); // expected output: 6

console.log(sum.apply(null, numbers)); // expected output: 6

#### function calls中的spread语法
##### spread语法可以替代apply
```js
function myFunction(x, y, z) { }
const args = [0, 1, 2];
// 可以使用apply将一个普通数组转换为函数的arguments
myFunction.apply(null, args);

有了spread语法后,可以这样写:

function myFunction(x, y, z) { }
const args = [0, 1, 2];
myFunction(...args);

spread语法可以用很多次:

function myFunction(v, w, x, y, z) { }
const args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);
new操作符中的spread语法

apply有[[Call]]没有[[Construct]]。

const dateFields = [1970, 0, 1];
const d = new Date(...dateFields);

若是没有spread 语法,需要按照下面的方式为new使用数组:很复杂。

array literals中的spread语法

基于原有数组创建新数组更方便,不用组合使用push,splice,concat
const parts = ['shoulders', 'knees']; 
const lyrics = ["head", ...parts, "and", "toes"];
//  ["head", "shoulders", "knees", "and", "toes"]
复制一个数组
const arr = [1, 2, 3];
const arr2 = [...arr]; // 与arr.slice()效果一样

arr2.push(4); // arr2 [1,2,3,4] arr [1,2,3]
更好的连接数组的方式
const arr1 = [0, 1, 2];
const arr2 = [3, 4, 5];

//  Append all items from arr2 onto arr1
arr1 = arr1.concat(arr2);

使用spread 语法后:

const arr1 = [0, 1, 2];
const arr2 = [3, 4, 5];

arr1 = [...arr1, ...arr2];

将数组的每一项都放置在某个数组前面:Array.prototype.unshift()

let arr1 = [0, 1, 2];
const arr2 = [3, 4, 5];

//  Prepend all items from arr2 onto arr1
Array.prototype.unshift.apply(arr1, arr2) 

//  arr1 is now [3, 4, 5, 0, 1, 2]

用spread语法后,会异常简单:

let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];

arr1 = [...arr2, ...arr1]; 
//  arr1 is now [3, 4, 5, 0, 1, 2]

在可迭代对象上的spread

对象的浅拷贝和合并,有了比Object.assign()更加方便的方法。

const obj1 = { foo: 'bar', x: 42 };
const obj2 = { foo: 'baz', y: 13 };

const clonedObj = { ...obj1 };
// Object { foo: "bar", x: 42 }

const mergedObj = { ...obj1, ...obj2 };
// Object { foo: "baz", x: 42, y: 13 }

Object.assign()会出发setters,而spread语法不会。

spread语法仅仅作用于iterables

Objects本身不可迭代,只有一下几种才迭代:

下面的方式使用会报错:

const obj = {'key1': 'value1'};
const array = [...obj]; // TypeError: obj is not iterable

Rest syntax与Spread syntax

FrankKai commented 4 years ago

...操作符实践

通过...操作符克隆出的数组和对象,是浅拷贝还是深拷贝?

亲测。浅拷贝

数组验证:

var foo = [1,2,3,{hi:'es5'}];
var bar = [...foo];
bar[3].hi = 'es6';
bar; // [1,2,3,{hi:'es6'}]
foo; // [1,2,3,{hi:'es6'}] 注意这里,由于...是浅拷贝,所以foo也跟着变成es6了

对象验证:

var foo = {hi: {version:'es5'}};
var bar = [...foo];
bar.hi.version = 'es6';
bar; // {hi: {version:'es6'}};
foo; // {hi: {version:'es6'}}; 注意这里,由于...是浅拷贝,所以foo也跟着变成es6了
Math.max,Math.min取最大最小值
const arr = [2,1,3];
// 取最大值
Math.min(...arr)
// 取最小值
Math.max(...arr)

函数形参为 对象类型 解构

let obj = {foo: 1, bar: 2, baz: 3, oof: 4}
function destructObj({foo, bar, ...others}){
    console.log(foo, bar, others);
}
destructObj(obj);
// 1 2 {baz: 3, oof: 4}

函数形参为 数组类型 解构

let arr = [1,2,3,4,"foo"]
function destructArr([num1, num2, ...others]){
    console.log(num1, num2, others);
}
destructArr(arr);
// 1 2 [3, 4, "foo"]
FrankKai commented 4 years ago

总结与思考