# ES6 剩余参数 和 展开运算符在实际开发中的应用
TIP
本节内容我们开始学习 ES6 中的 剩余参数、数组的展开运算符、对象的展开运算符,以及在实际开发中的注意事项和应用。
剩余参数
- 剩余参数是什么 ?
- 剩余参数在实际开发中的注意事项
- 剩余参数在实际开发中的应用
数组的展开运算符
- 数组展开运算符的基本用法
- 区分扩展运算符和剩余参数
- 数组展开运算符在实际开发中的应用
对象的展开运算符
- 什么是对象的扩展运算符
- 对象展开运算符在实际开发中的注意事项
- 对象扩展运算符在实际开发中的应用
# 一、剩余参数
TIP
深入浅出什么是剩余参数,剩余参数的本质。
# 1、什么是剩余参数
TIP
ES6 中引入了 rest(剩余)参数,允许将不确定部分的参数合成一个数组。其写法为 ...变量名
,变量是一个数组,用来保存不确定部分的参数。
简单理解:rest 参数,就是把用逗号隔开的值合成一个数组,保存在变量中。
// ...args 为剩余参数,其中变量args表示一个数组,用来接受传过来的所有实参
const sum = (...args) => {
console.log(args);
};
sum(1, 2, 3, 4); // [1, 2, 3, 4]
sum(); // []
// ...args 为剩余参数,其中变量args表示一个数组,用于接受第3个(包含)之后的所有实参
const f = (a, b, ...args) => {
console.log(a); // 1
console.log(b); // 2
console.log(args); // [3, 4, 5]
};
f(1, 2, 3, 4, 5);
注:
我们要确定剩余参数对应的参数,先要把确定的参数排除,剩下的不确定参数就全部归剩余参数。
# 2、剩余参数的本质
TIP
剩余参数中的变量本质是一个数组,即使没有值,也是一个空数组,所以数组所有的方法这个变量都可以用
const f = (a, ...args) => {
args.push(a);
console.log(args);
};
f(1, 2, 3, 4, 5);
# 3、剩余参数的注意事项
- 剩余参数之后不能再有其它参数,即剩余参数必需是最后一个参数,否则会报错
const f = (a,...args,b)=>{} // 报错
- 当箭头函数中只有一个剩余参数时,其圆括号也不能省略
// const f = ...args => args; 错误写法
const f = (...args) => args;
console.log(f(1, 2, 3)); // [1, 2, 3]
- 在箭头头函数中没有 arguments,所以 rest 剩余参数可以在箭头或普通函数中替代 arguments 来接受对应的实参列表。
- arguments 是类数组对象,很多时候要把他转换为数组再用,而剩余参数本身就是数组,用起来就很方便
在以后的开发中,我们常会用 rest 剩余参数来代替 arguments
// 普通函数与arguments
function sum() {
let result = 0; // 累加器
Array.prototype.forEach.call(arguments, (item) => {
result += item;
});
return result;
}
console.log(sum(1, 2, 3, 4));
// 箭头函数与剩余参
const sum2 = (...args) => {
let result = 0; // 累加器
args.forEach((item) => {
result += item;
});
return result;
};
console.log(sum2(1, 2, 3, 4));
- 函数的 length 属性用来保存未设置默认值参数的个数,rest 剩余参数不计入其中
const f = (a, b, c = 2, ...args) => {};
console.log(f.length); // 2
总结
注意事项 | 说明 |
---|---|
剩余参数的位置 | 剩余参数必须是最后一个参数 |
箭头函数中只有一个剩余参数 | 箭头函数中只有一个剩余参数,也不能省略圆括号 |
剩余参数与 arguments | 箭头函数中没有 arguments,在往后的开发中,我们会利用剩余参数来替代 arguments,因为剩余参数本身就是一个数组,用起来更方便 |
函数 length 属性不计算 rest 参数 | 函数的 length 属性用来保存未设置默认值参数的个数,rest 剩余参数不计入其中 |
# 4、剩余参数在实际开发中的应用
- 根据实参传递的个数来求和
// 箭头函数与剩余参
const sum = (...args) => {
var result = 0;
args.forEach((item) => {
result += item;
});
return result;
};
console.log(sum(1, 2, 3, 4));
- 剩余参数与数组的解构赋值的结合应用
剩余参数不一定非要作为函数参数使用,解构赋值也可以使用
当剩余参数与解构赋值结合使用时,我们称他为 剩余元素。
同时在与解构赋值结合时,也只能是最后一个元素,之后不能再有其他参数,否则会报错
// 绑定模式
let [a, b, ...c] = [1, 2, 3, 4, 5, 6];
console.log(a); // 1
console.log(b); // 2
console.log(c); // [3, 4, 5, 6]
// 赋值模式
let a, b, c;
[a, b, ...c] = [1, 2, 3, 4, 5, 6];
console.log(a); // 1
console.log(b); // 2
console.log(c); // [3, 4, 5, 6]
- 剩余参数与与对象的解构赋值结合应用
重点提示: 剩余元素与对象解构赋值结合时,其不数组,而是对象。
同时解构赋值时,剩余元素不会复制原型对象上的属性
// 绑定模式
const { a, b, ...c } = { a: 1, b: 2, c: 3, d: 5 };
console.log(a); // 1
console.log(b); // 2
console.log(c); // {c: 3, d: 5}
// 赋值模式
const a, b, c;
({ a, b, ...c } = { a: 1, b: 2, c: 3, d: 5 });
console.log(a); // 1
console.log(b); // 2
console.log(c); // {c: 3, d: 5}
- 我们来看下面这种情况,以下 args 是剩余参数还是剩余元素
function fn([a, ...args1], ...args2) {
console.log(args1); // [2, 3, 4] 剩余元素
console.log(args2); // ['A', 'B'] 剩余参数
}
fn([1, 2, 3, 4], "A", "B");
总结:
- 剩余参数和剩余元素的书写格式都是使用
…变量名
的方式 - 不同的是:剩余参数是直接作为函数参数使用的。剩余元素是在解构赋值时,用来接收剩下的元素。
# 二、数组的扩展(展开)运算符
TIP
深入浅出展开运算符,数组展开运算符的基本用法等。
# 1、什么是数组扩展运算符
TIP
数组扩展运算符(spread)是三个点...
,他可以将数组展开为用逗分隔的参数序列。
const arr = [1, 2, 3];
// ...arr 相当于把数组展开以逗号分隔的序列 console.log(1,2,3)
console.log(...arr); // 1 2 3
console.log(1, ...["A", "B", "C"], 2); // 1 'A' 'B' 'C' 2
- 如果扩展运算符后面是一个空数组,则不会产生任何效果
const arr = [1, 2, ...[], 3];
console.log(arr); // [1, 2, 3]
- 数组扩展运算符后面可以放置表达式
let a = 1;
let b = 3;
const arr = [...(a > b ? [a] : [b])];
console.log(arr); // [3]
# 2、区分扩展运算符和剩余参数
TIP
我们上边学习的展开运算符,发现它和剩余参数在语法上是一样的,都是前边加上 ...
这样就会导致我们经常分不清楚。
接下来我们来看剩余参数和展开运算符的根本区别。
- 展开运算符:展开运算符是将数组展开成一个个以逗号分隔的序列。
[6,2,3] -> 6,2,3
- 剩余参数:将参数列表的形式转换成数组形式
6,2,3 -> [6,2,3]
- 区分以下代码中,哪些是扩展运算符,哪些是剩余参数
const sum = (...args) => {
// ...args 剩余参数 用来接受数组传过来的所有实参
console.log(args); // [6, 2, 3]
// 展开运算符,...args 相当于 ...[6, 2, 3] 将数组展开成 6,2,3
console.log(...args); // 6 2 3
};
sum(6, 2, 3);
# 3、测试题
TIP
以下代码中的第一个...arg1
、...arg2
、第二个...arg1
,分别是表示什么 ?
const f = ([...arg1], ...arg2) => {
console.log(arg2);
console.log(...args1);
};
f([1, 2, 3], "A", "B", ["x", "y"]);
说明
- 第一个
...arg1
,是在数组中,相当于let [...arg1] = [1,2,3]
,所以第一个arg1
是剩余元素 ...arg2
是写在函数的参数列表中,所以是剩余参数,用来接受除第一个参数之外的其它参数- 第二个
...arg1
,是展开运算符,用来将第一个arg1
对应的数组展开。
# 4、数组展开运算符在实际开发中的应用
- 复制数组
// 复制数组
const arr = [1, 2, 3, 4, 5];
// 展开运算符复制数组
const arr1 = [...arr];
console.log(arr1); // [1, 2, 3, 4, 5]
复制数组本质:在原来的数组中展开数组即可,展开也是浅复制
- 合并数组
// 合并数组
const x = [1];
const y = [2, 3];
const z = [5, 6, 8];
// 展开运算符合并数组
console.log([...x, ...y, ...z]); // [1, 2, 3, 5, 6, 8]
// 随意调换顺序
console.log([...y, ...x, ...z]); // [2, 3, 1, 5, 6, 8]
console.log([...z, ...x, ...y]); // [5, 6, 8, 1, 2, 3]
// 还可以根据自己的需求增加数组的值
console.log([...z, 10, ...x, 20, ...y, 30]); // [5, 6, 8, 10, 1, 20, 2, 3, 30]
- 字符串转为数组
字符串可以按照数组的形式展开
// 字符串可以看做是一个类数组,因此字符串是可以按照数组的形式展开
const str = "icoding";
// 使用展开运算符将字符串展开
console.log(...str); // i c o d i n g
// 将字符串转为数组,只需要将展开字符串放到数组[]中即可
const arr = [...str];
console.log(arr); // ['i', 'c', 'o', 'd', 'i', 'n', 'g']
为什么要将字符串转数组呢 ?
因为,数组中有很多好用的方法,我们就可以把字符串转为数组,即可使用数组的方法。用完之后再把数组转为字符串,这样就非常方便了。
- 常见的类数组转化为数组
arguments 类数组
function foo() {
// 在数组中展开 arguments 即可 将类数组转为数组
console.log([...arguments]);
}
foo(1, 2, 3); // [1, 2, 3]
NodeList 类数组
// NodeList 是类数组没有数组相关方法
const p = document.querySelectorAll("p");
// 使用展开运算符转为数组
const arr = [...p];
console.log(arr); // [p, p, p]
重点提示: 任何
Iterator
接口的对象都可以用扩展运算符转为真正的数组。
- 将数组转换为函数的参数
由于扩展运算符可以展开数组,所以不再需要使用 apply 方法将一个数组转为函数的参数了。可以直接用扩展运算符来实现。
案例:求数组中的最大值和最小值
// 先来复习两个Math对象上的方法
let max = Math.max(1, 2, 5); // 返回参数列表中的最大值
let min = Math.min(1, 2, 5); // 返回参数列表中的最小值
console.log(max, min); // 5 1
没有扩展运算符之前,实现代码如下
const arr = [17, 5, 1, 8, 3, 20];
let max = Math.max.apply(null, arr);
let min = Math.min.apply(null, arr);
console.log(max, min); // 20 1
利用扩展运算符实现,代码如下
const arr = [17, 5, 1, 8, 3, 20];
let max = Math.max(...arr);
let min = Math.min(...arr);
console.log(max, min); // 20 1
# 三、对象的扩展(展开)运算符
TIP
深入浅出对象展开运算符的基本用法,注意事项,以及在实际开发中的应用。
# 1、什么是对象的扩展运算符
TIP
扩展运算符...
用于取出对象的所有可遍历属性,并将其复制到当前对象之中。
也就是说 对象不能直接展开或在数组中展开,必需在
{}
对象中展开。
const student = {
username: "icoding",
age: 18,
sex: "male",
addr: "北京",
};
console.log({ ...student }); // {username: 'icoding', age: 18, sex: 'male', addr: '北京'}
// 对象不能使用以下方式展开
console.log(...student); // 报错
console.log([...student]); // 报错
const obj = {
a: 1,
...{ b: 2, c: 3 },
d: 4,
};
console.log(obj); // {a: 1, b: 2, c: 3, d: 4}
# 2、对象扩展运算符的注意事项
- 空对象的展开
如果展开一个空对象,没有任何效果
console.log({ ...{} }); // {}
// 空对象 和 属性的合并
console.log({ ...{}, x: 123 }); // {x: 123}
- 非对象的展开,自动转换为对象
如果展开的不是对象,则会自动将其转为对象,再将其属性罗列出来
// 数值类型
console.log({ ...2 }); // {}
// undefined
console.log({ ...undefined }); // {}
// null
console.log({ ...null }); // {}
// boolean
console.log({ ...true }); // {}
对象扩展运算符的参数是 null 或 undefined 时,这两个值会被忽略,但不会报错
// 对象中展开运算符后边是字符串
console.log({ ..."arry" }); // {0: 'a', 1: 'r', 2: 'r', 3: 'y'}
// 数组中展开运算符后边是字符串
console.log([..."arry"]); // ['a', 'r', 'r', 'y']
// 直接展开字符串
console.log(..."arry"); // a r r y
// 对象中展开运算符后边是数组
console.log({ ...[1, 2, 3] }); // {0: 1, 1: 2, 2: 3}
- 对象展开属于浅复制(浅拷贝)
对象展开的本质:就是把对象的属性复制罗列出来,用逗号分隔,放到一个
{}
中,不过在复制属性时,属于浅复制(浅拷贝)
const obj = {
a: 1,
b: 2,
arr: ["A", "B", "C"],
};
// 将obj在一个对象中展开
const obj2 = { ...obj };
console.log(obj2); // {a: 1, b: 2, arr: ["A", "B", "C"]}
console.log(obj === obj2); // false
obj.arr.push("D");
console.log(obj2); // {a: 1, b: 2, arr: ["A", "B", "C","D"]}
console.log(obj.arr === obj2.arr); // true
// 当我们给obj中的arr数组后面添加一个新的元素"D"时,obj2中的arr数组中的元素也更改了,本质上obj.arr与obj2.arr 是完全相等的
- 合并对象,同名属性会覆盖
如果展开的对象中有与现有对象中同名的属性,则会发生覆盖。覆盖原则以写在后面的覆盖前面。
const obj = {
a: 1,
b: 2,
c: 3,
};
const obj2 = {
a: "清心",
...obj,
c: "icoding",
};
console.log(obj2);
// obj中的属性a覆盖了obj2中同名的a,obj2中的属性c,覆盖了obj中同名的属性c
- 扩展运算符的参数对象之中如果有取值函数 get,这个函数将会被执行
let obj = {
_x: 1,
get x() {
return this._x;
},
set x(value) {
if (value === 3) {
this._x = "不通过";
} else {
this._x = value;
}
},
};
const obj2 = { ...obj };
console.log(obj2); // {_x: 1, x: 1}
obj2.x = 3; // 丢失了set函数的功能
console.log(obj2); // {_x: 1, x: 3}
总结
注意事项 |
---|
空对象的展开,没有任何效果 |
非对象类型展开,会先转换为对象,然后再将其可遍历属性罗列出来 |
对象展开属于浅复制(浅拷贝) |
合并对象,同名属性会覆盖,覆盖原则为写在后面的覆盖前面的 |
扩展运算符的参数对象之中如果有取值函数 get,这个函数将会被执行 |
# 3、对象展开运算符在实际开发中的应用
- 复制对象(浅复制)
const x = { a: 1, b: 2 };
// 使用展开运算符复制对象
const y = { ...x };
console.log(y); // {a: 1, b: 2}
console.log(y === x); // false 只是复制对象
- 用户参数与默认参数合并
如果我们想写一个提供给用户使用的函数或方法时,就会涉及用户参数和默认参数。即:
- 用户参数: 是用户调用方法时实际传递的参数都叫做用户参数,也就是我们之前提到的实参。
- 默认参数: 如果函数参数有三个,用户只传递了两个,剩余的默认参数就会生效,这样可以让用户少传递不必要的参数,可以让用户使用函数变得更方便
// 之前讲过的做法,给对象设置默认值 {}
const person = ({ username = "icoding", age = 0, sex = "male" } = {}) => {
console.log(username, age, sex);
};
person(); // icoding 0 male // 默认值生效
person({ username: "arry" }); // arry 0 male
通过以上方式是可以解决用户参数和默认参数问题的。这也是其中一种方式
还可以采用如下方式:
// userParam 用户参数
const person = (userParam) => {
// 统一用一个对象的形式来设置 默认参数
const defaultParam = {
username: "icoding",
age: 0,
sex: "male",
};
// 最终的参数,将用户参数和默认参合并
// 以用户参数为准,用户参数没有再使用默认参数,因此将userParam写到后边
// const param = { ...defaultParam, ...userParam };
// console.log(param.username, param.age, param.sex);
// 使用对象解构赋值,变得更简单
const { username, age, sex } = { ...defaultParam, ...userParam };
console.log(username, age, sex);
};
person(); // icoding 0 male 默认值生效
person({ username: "arry" }); // arry 0 male
# 四、重难点总结
TIP
总结本章重难点知识,理清思路,把握重难点。并能轻松回答以下问题,说明自己就真正的掌握了。
用于故而知新,快速复习。
# 1、区分剩余参数与展开运算符
分类 | 说明 |
---|---|
剩余参数 | 用作函数的形参,用来接收函数的剩余参数。剩余参数是一个数组,如果没有值为空数数组[] |
剩余元素 | 用于对象和数组的解构赋值,表示接收数组的剩余元素或对象的剩余属性(不包括原型上的属性) |
数组展开运算符 | 在数组、类数组、字符串前添加... ,可将一个其展开成用逗号分隔的序列。 |
对象展开运算符 | 在对象前添加... ,可以将对象在另一个对象中展开。展开的本质是将对象中的可遍历属性罗列出来,用逗号分隔,放到一个{} 对象中。对象展开运算符只能在对象中展开,在其它地方展开会报错。 |
# 2、剩余参数的注意事项
注意事项 | 说明 |
---|---|
剩余参数的位置 | 剩余参数必须是最后一个参数 |
箭头函数中只有一个剩余参数 | 箭头函数中只有一个剩余参数,也不能省略圆括号 |
剩余参数与 arguments | 箭头函数中没有 arguments,在往后的开发中,我们会利用剩余参数来替代 arguments,因为剩余参数本身就是一个数组,用起来更方便 |
函数 length 属性不计算 rest 参数 | 函数的 length 属性用来保存未设置默认值参数的个数,rest 剩余参数不计入其中 |
# 3、对象展开运算符的注意事项
注意事项 |
---|
空对象的展开,没有任何效果 |
非对象类型展开,会先转换为对象,然后再将其可遍历属性罗列出来 |
对象展开属于浅复制(浅拷贝) |
合并对象,同名属性会覆盖,覆盖原则为写在后面的覆盖前面的 |
扩展运算符的参数对象之中如果有取值函数 get,这个函数将会被执行 |
注:
字符串比较特殊,它即可在对象中展开,也可以在数组织中展开,还可以在直接展开,这点与数组类似。
# 4、应用
TIP
剩余参数、数组的扩展运算符、对象的扩展运算符的应用案例,必需要掌握
# 五、测试题
TIP
自我测试:在不看答案的前提下,看看自己是否真正掌握了本节所学内容。
# 1、以下关于剩余参数描述错误的选项是 ?
单选
- A、剩余参数本质是一个数组
- B、剩余参数的书写位置是任意的
- C、普通函数可以使用剩余参数获取所有的参数
- D、剩余参数不计入函数的 length 个数
自己先分析,再点击查看正确答案
正确答案:B
答案解析: 剩余参数只能是函数的最后一个参数
# 2、下面代码中,args 表示剩余参数的选项是 ?
两项
A、
const sum = ([a, b, ...args]) => {};
B、
const sum = (a, b, [...args]) => {};
C、
const sum = ({ a, ...c }, ...args) => {};
D、
const sum = ([...args]) => {};
自己先分析,再点击查看正确答案
正确答案:C
答案解析: A、B、C 选项中的 args 参于数组解构赋值,args 为剩余元素。
# 3、以下代码的输入结果是 ?
const obj1 = {
x: 1,
y: 2,
a: 3,
};
const obj2 = {
a: 1,
b: 2,
c: 3,
};
const obj3 = {
b: 6,
y: 5,
d: 4,
};
const o = { ...obj1, ...obj2, ...obj3 };
console.log(o);
自己先分析,再点击查看正确答案
正确答案:{x: 1, y: 5, a: 1, b: 6, c: 3,d:4}
答案解析:对象中相同属性名,写在后面的会覆盖前面的。
大厂最新技术学习分享群
微信扫一扫进群,获取资料
X