目录
一、扩展运算符(spread)
1、扩展运算符的语法
2、扩展运算符特性
2、扩展运算符与字符串
3、扩展运算符与数组
4、扩展运算符与对象
5、扩展运算符与函数
二、剩余运算符(rest)
1、rest 运算符的语法
2、rest 运算符的特性
3、rest 运算符的用途
扩展运算符用三个点号表示(...),用来:将一个可迭代对象展开为空格分隔的值序列。
console.log(...[1, 2, 3]);// 1 2 3
①、扩展运算符内部使用for...of循环,可以将一个“可迭代对象”转为用空格分隔的参数序列。
console.log(...'hello')// h e l l o
console.log(...[1, 2, 3]);// 1 2 3
console.log(...{0:"a", 1:"b", 2:"c"});// TypeError: Found non-callable @@iterator
②、扩展运算符后面 可以放置表达式。
var x = 10;
const arr = [
...(x > 0 ? ['a'] : []),
'b',
];
const obj = {
...(x > 1 ? {a: 1} : {}),
b: 2,
};
console.log(arr);// ["a", "b"]
console.log(obj);// {a: 1, b: 2}
③、扩展运算符只能操作“可迭代对象”,不能操作 “类数组对象”。
--> 可迭代对象 和 类数组对象
常见的 可迭代对象 包括:
--> 用扩展运算符操作 “可迭代对象”
/* Map 结构 */
let map = new Map();
map.set('key0', 'value0');
map.set('key1', 'value1');
Array.from(map); // [['key0', 'value0'],['key1', 'value1']]
/* Set 结构 */
let arr = [1, 2, 3];
let set = new Set(arr);
Array.from(set); // [1, 2, 3]
/* 字符串 */
[...'hello'];// [ "h", "e", "l", "l", "o" ]
/* arguments对象 */
function foo() {
console.log(arguments); // Arguments(6) [1, 2, 3, 4, 5, 6, callee: ƒ, Symbol(Symbol.iterator): ƒ]
console.log(Array.isArray(arguments)); // false
// ES5 中支持这样将arguments转为数组
console.log(Array.prototype.slice.call(arguments)); // [1, 2, 3, 4, 5, 6]
// ES6 中还能这样将arguments转为数组
console.log([...arguments]); // [1, 2, 3, 4, 5, 6]
}
foo(1, 2, 3, 4, 5, 6);
/* NodeList对象 */
var nodList = document.querySelectorAll('div');
console.log(nodList); // NodeList(3) [div, div, div]
console.log(Array.isArray(nodList)); // false
console.log([...nodList]); // [div, div, div]
--> 扩展运算符不能将“类数组对象”转为数组,可以采用 Array.from() 方法将“类数组对象”转为数组。
扩展运算符不能将“类数组对象”转为数组,否则报错:
let obj = {
a: "1",
b: "2",
length: 2
};
console.log(...obj); // TypeError: Found non-callable @@iterator
可以采用 Array.from() 方法将“类数组对象”转为数组:
let str = "hello";
console.log(str.length); // 5
console.log(Array.from(str)); // ['h', 'e', 'l', 'l', 'o']
不过,别用 Array.from() 方法将对象转为数组,详见案例:
// 没有length属性的对象会被转为空数组
let obj = {
a: "1",
b: "2"
};
console.log(Array.from(obj)); // []
console.log(...obj); // TypeError: Found non-callable @@iterator
// 有length属性的对象会被转为包含length个undefined的数组
let obj = {
a: "1",
b: "2",
length: 2
};
console.log(Array.from(obj)); // [undefined, undefined]
console.log(...obj); // TypeError: Found non-callable @@iterator
console.log(...'hello')// h e l l o
console.log([...'hello'])// ["h", "e", "l", "l", "o"]
console.log({...'hello'})// {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}
①、如果扩展运算符后面是一个空数组,有以下三种情况:
console.log(...[])//
console.log([...[]])// []
console.log([...[], 1])// [1]
②、复制数组
const a1 = [1, 2];
const a2 = [...a1];
const [...a2] = a1;
③、合并数组
const a1 = [1, 2];
const a2 = [3, 4];
const a3 = [...a1, ...a2];// [1, 2, 3, 4]
④、将一个数组插入到另一个数组中
const a1 = [1, 2];
const a2 = [...a1, 3, 4];// [1, 2, 3, 4]
①、对象里的扩展运算符等同于使用Object.assign()方法。
let aClone = { ...a };
// 等同于
let aClone = Object.assign({}, a);
②、如果扩展运算符后面是一个空对象,有以下三种情况:
console.log(...{});// TypeError: Found non-callable @@iterator
console.log({...{}});// {}
console.log({...{}, a: 1});// {a: 1}
③、数组是特殊的对象,可以用扩展运算符直接将数组转为对象。
console.log({...[1, 2, 3]});
// {0: 1, 1: 2, 2: 3}
④、如果扩展运算符后面不是对象,则会自动将其转为一个空对象。
console.log({...1}); // {}
console.log({...true});// {}
console.log({...undefined});// {}
console.log({...null});// {}
⑤、扩展运算符可以用于合并两个对象。
let a = {a:"111"};
let b = {b:"222"};
console.log({ ...a, ...b });// {a: "111", b: "222"}
// 等同于
let ab = Object.assign({}, a, b);
⑥、如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉。
let a = {x: 3, y: 6, z: 9}
let b = { ...a, x: 1, y: 2 };// {x: 1, y: 2, z: 9}
// 等同于
let c = { ...a, ...{ x: 1, y: 2 } };
// 等同于
let x = 1, y = 2, d = { ...a, x, y };
// 等同于
let e = Object.assign({}, a, { x: 1, y: 2 });
⑦、如果把自定义属性放在扩展运算符前面,就变成了设置新对象的默认属性值。
let a = {z: 9}
let b = { x: 1, y: 2, ...a };// {x: 1, y: 2, z: 9}
// 等同于
let c = Object.assign({}, { x: 1, y: 2 }, a);
// 等同于
let d = Object.assign({ x: 1, y: 2 }, a);
①、只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错。
(...[1, 2]); // Uncaught SyntaxError: Unexpected number
console.log(...[1, 2]); // 1 2
②、用扩展运算符解构“可迭代对象”作为函数的实参。
扩展运算符(spread)与剩余运算符(rest)虽然都是三个点(...),但它们在函数中的使用完全不同,与 “rest 参数”(下文会讲到)相比:
--> spread 运算符用于控制传入函数的 实参:
function add(x, y) {
return x + y;
}
const numbers = [4, 38];
add(...numbers) // 42
--> rest 运算符用于控制传入函数的 形参:
function fn(...arr){
console.log(arr);
}
fn(1, 2, 3, 4, 5, 6);// [1, 2, 3, 4, 5, 6]
③、扩展运算符的参数对象之中,如果有取值函数get,这个函数是会执行的。
let a = {
get x() {
console.log("hello world!");
}
}
let aWithXGetter = { ...a }; // "hello world!"
剩余运算符也是用三个点号表示(...),与扩展运算符相反,剩余运算符用来:将逗号隔开的值序列组合成一个数组或对象。
与扩展运算符相比,利用剩余运算符得到的对象不一定是 “可迭代对象”。
// 用剩余运算符解构数组
var [a,...temp]=[1, 2, 4];
temp;// [2, 4]
console.log(...temp);// 2 4
// 用剩余运算符解构对象
let { x, ...y } = { x: 1, a: 2, b: 3 };
x // 1
y // { a: 3, b: 3 }
console.log(...y);// TypeError: Found non-callable @@iterator
ES2016 用 rest 运算符 定义了 rest 参数,该参数的形式为 “...变量名” 。该参数主要用于获取函数的多余参数。
①、rest 参数只能作为函数的最后一个参数
function f(a, ...b, c) {
// ...
}
// SyntaxError: Rest parameter must be last formal parameter
②、rest 参数搭配的变量是一个数组
function add(...values) {
console.log(values);
}
add(2, 5, 3) // [2, 5, 3]
③、函数的 length 属性不包括 rest 参数
(function(...a) {}).length // 0
(function(a, ...b) {}).length // 1
④、rest 参数取代了 arguments 对象
有了 rest 参数,就不需要使用 arguments 对象了。下面是一个 rest 参数代替 arguments 变量的例子:
// Arguments 写法
function foo1() {
console.log([].slice.call(arguments));
}
foo1(1, 2, 3, 4, 5, 6); // [1, 2, 3, 4, 5, 6]
// rest 写法
function foo2(...rest) {
console.log(rest);
}
foo2(1, 2, 3, 4, 5, 6); // [1, 2, 3, 4, 5, 6]
上述代码中,arguments对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用Array.prototype.slice.call先将其转为数组( “[]” 等价于 “Array.prototype” )。rest 参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。 所以 rest 参数更方便实用,完全可以取代 arguments 了。
⑤、使用了 rest 参数的函数内部不能显示设定严格模式,否则会报错
只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。
// 报错
const doSomething = (...a) => {
'use strict';
// code
};
其他 “严格模式” 的案列,请戳此链接:https://blog.csdn.net/mChales_Liu/article/details/106905560
①、rest 运算符 配合 解构 使用
// 用于数组
var [a,...temp]=[1, 2, 4];
a;// 1
temp;// [2, 4]
// 用于对象
let { x, ...y } = { x: 1, a: 2, b: 3 };
x // 1
y // { a: 3, b: 3 }
②、当函数参数个数不确定时,用 rest 参数作为函数的形参
当函数参数个数不确定时,用 rest 参数 作为函数的形参,会把未来传入函数的参数整合成一个数组。
// 操作数组
function fn(...arr){
console.log(arr);
}
fn(1, 2, 3, 4, 5, 6);
// [1, 2, 3, 4, 5, 6]
// 操作对象
function baseFunction({ a, b }) {
// ...
}
function wrapperFunction({ x, y, ...restConfig }) {
// 使用 x 和 y 参数进行操作
// 其余参数传给原始函数
return baseFunction(restConfig);
}