Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

es6之rest parameters和spread syntax #174

Open
FrankKai opened this issue Feb 18, 2020 · 4 comments
Open

es6之rest parameters和spread syntax #174

FrankKai opened this issue Feb 18, 2020 · 4 comments

Comments

@FrankKai
Copy link
Owner

FrankKai commented Feb 18, 2020

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

  • Rest parameters
    • rest parameters语法
    • rest parameters与arguments的区别
    • rest parameters没出现前,es5怎么做?
    • rest parameters既然是数组,可以直接解构吗?
    • 优雅的使用rest parameters的例子
  • Spread syntax
    • 几种spread场景
    • function calls中的spread语法
    • array literals中的spread语法
    • 在可迭代对象上的spread
    • Rest syntax与Spread syntax
  • ...操作符实践
    • 通过...操作符克隆出的数组和对象,是浅拷贝还是深拷贝?
    • Math.max,Math.min取最大最小值
    • 函数形参为对象类型结构
    • 函数形参为数组类型结构
  • 总结与思考
@FrankKai
Copy link
Owner Author

FrankKai commented Feb 18, 2020

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仅仅包含了那些没有被命名的剩下的形参,而arguments包含了全部的形参
  • arguments是一个类数组但不是数组,rest parameters是Array实例,意味着可以使用sort,map,forEach等等方法
  • arguments对象有额外的属性,例如callee

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
Copy link
Owner Author

FrankKai commented Feb 18, 2020

Spread syntax

几种spread场景

  • function calls 展开arguments myFunction(...iterableObj);
  • array or strings literals 展开元素 [...iterableObj, '4', 'five', 6];
  • object literals 展开key-value对 es2018 let objClone = { ...obj };
function sum(x, y, z) {
  return x + y + z;
}
const numbers = [1, 2, 3];

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

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

function calls中的spread语法

spread语法可以替代apply
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本身不可迭代,只有一下几种才迭代:

  • 在Array中使用
  • 使用迭代函数map(), reduce(), assign()
  • 合并两个对象时,会假设另一个迭代函数被使用了

下面的方式使用会报错:

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

Rest syntax与Spread syntax

  • rest看起来相同,但是rest主要用于解构数组和对象。
  • rest parameters是spread syntax的对立面。spread是扩展,rest是收集。
  • spread syntax “展开”一个数组到多个元素;rest parameters收集多个元素到一个元素中。

@FrankKai
Copy link
Owner Author

FrankKai commented Feb 18, 2020

...操作符实践

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

亲测。浅拷贝

数组验证:

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
Copy link
Owner Author

FrankKai commented Apr 4, 2020

总结与思考

  • rest parameter是收集剩余,快捷转换arguments为数组
  • spread syntax是展开组合,替代apply,向new构造器传值,快捷复制和重组数组和对象
  • 二者都是以...的形式存在,无须过于关注其区别,关注如何简化代码,关注如何使得代码更优雅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant