js ES6扩展运算符(spread)和剩余运算符(rest)

目录

一、扩展运算符(spread)

1、扩展运算符的语法

2、扩展运算符特性

2、扩展运算符与字符串

3、扩展运算符与数组

4、扩展运算符与对象

5、扩展运算符与函数

二、剩余运算符(rest)

1、rest 运算符的语法

2、rest 运算符的特性

3、rest 运算符的用途


一、扩展运算符(spread)

1、扩展运算符的语法

扩展运算符用三个点号表示(...),用来:将一个可迭代对象展开为空格分隔的值序列

console.log(...[1, 2, 3]);// 1 2 3

2、扩展运算符特性

  • 扩展运算符内部使用for...of循环,可以将一个“可迭代对象”转为用空格分隔的参数序列。
  • 扩展运算符后面 可以放置表达式。
  • 扩展运算符只能操作“可迭代对象”,不能操作 “类数组对象”。

①、扩展运算符内部使用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}

③、扩展运算符只能操作“可迭代对象”,不能操作 “类数组对象”。

--> 可迭代对象 和 类数组对象

  1. 可迭代对象,指的是:定义了遍历器(Iterator)接口的对象。
  2. 类数组对象,指的是:具有 length 属性的对象。

常见的 可迭代对象 包括:

  • Array
  • Map
  • Set
  • String
  • ES5 中的函数的 arguments 对象
  • NodeList 对象
  • Generator 函数

--> 用扩展运算符操作 “可迭代对象” 

/* 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

2、扩展运算符与字符串

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"}

3、扩展运算符与数组

①、如果扩展运算符后面是一个空数组,有以下三种情况:

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]

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);

5、扩展运算符与函数

①、只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错。

(...[1, 2]);               // Uncaught SyntaxError: Unexpected number
console.log(...[1, 2]);    // 1 2

②、用扩展运算符解构“可迭代对象”作为函数的实参。

扩展运算符(spread)与剩余运算符(rest)虽然都是三个点(...),但它们在函数中的使用完全不同,与 “rest 参数”(下文会讲到)相比:

  • spread 运算符用于控制传入函数的实参;
  • 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!"

 

二、剩余运算符(rest)

1、rest 运算符的语法

剩余运算符也是用三个点号表示(...),与扩展运算符相反,剩余运算符用来:逗号隔开的值序列组合成一个数组或对象

与扩展运算符相比,利用剩余运算符得到的对象不一定是 “可迭代对象”。

// 用剩余运算符解构数组
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 参数,该参数的形式为 “...变量名” 。该参数主要用于获取函数的多余参数。

2、rest 运算符的特性

  • rest 参数之后不能再有其他参数(即只能作为函数的最后一个参数),否则会报错。
  • rest 参数搭配的变量是一个数组或对象。
  • 函数的 length 属性不包括 rest 参数。
  • rest 参数取代了 arguments 对象。
  • 使用了 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

3、rest 运算符的用途

①、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);
}

你可能感兴趣的:(#,JavaScript,#,ES6)