电竞比分网-中国电竞赛事及体育赛事平台

分享

深入了解 ES6 強(qiáng)大的  ...  運(yùn)算符

 板橋胡同37號 2019-10-20
背景

... 運(yùn)算符,是 ES6 里一個(gè)新引入的運(yùn)算法,也叫 展開/收集 運(yùn)算符,我們每天都要和它打交道。
這篇文章,我就帶你系統(tǒng)的回顧下這個(gè)運(yùn)算符,介紹一些基礎(chǔ)和進(jìn)階的用法。
基礎(chǔ)篇
先看一下官方描述:
Spread syntax allows an iterable, such as an array expression or string, to be expanded in places where 0 or more arguments or elements are expected or an object expression to be expanded in places where 0 or more key-value pairs (for object literals) are expected.
簡而言之就是,... 運(yùn)算符可以展開一個(gè)可迭代對象重的所有項(xiàng)。
可迭代的對象一般是指可以被循環(huán)的,包括:string, array, set 等等。
下面我們來看幾個(gè)基礎(chǔ)的例子來加深理解。

基礎(chǔ)用法


基礎(chǔ)用法 1: 展開

const a = [2, 3, 4]
 const b = [1, ...a, 5]

 b; // [1, 2, 3, 4, 5]

基礎(chǔ)用法 2: 收集

function foo(a, b, ...c) {
    console.log(a, b, c)     
}

foo(1, 2, 3, 4, 5); // 1, 2, [3, 4, 5]
如果沒有命名參數(shù)的話,... 就會(huì)收集所有的參數(shù):
function foo(...args) {
    console.log(args)     
}

foo(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]
關(guān)于這個(gè)收集的用法,官方描述:
“A function’s last parameter can be prefixed with ... which will cause all remaining (user supplied) arguments to be placed within a 'standard' javascript array. Only the last parameter can be a rest parameter.”
這個(gè)運(yùn)算符一定是在最后一個(gè)參數(shù)的位置,也很好理解,就是“收集前面剩下的參數(shù)”。
Remember that the rest parameter must be the last parameter, or an error will occur.
如果不在最后一位,會(huì)報(bào)錯(cuò)。

不得不感嘆,這個(gè)運(yùn)算符設(shè)計(jì)的真的是妙,可展開,可收集,收放自如,當(dāng)真好用。

基礎(chǔ)用法 3: 把 類數(shù)組 轉(zhuǎn)換為 數(shù)組

先回顧下什么是類數(shù)組吧.

類數(shù)組和數(shù)組非常接近,都可以擁有一系列元素,也有l(wèi)ength 屬性,最大的不同是:

類數(shù)組不具備數(shù)組的一系列方法。

舉個(gè)例子:
const nodeList = document.getElementsByClassName('test');
const array = [...nodeList];

console.log(nodeList); //Result: HTMLCollection [ div.test, div.test ]
console.log(array); //Result: Array [ div.test, div.test ]

使用 ... 就可以實(shí)現(xiàn)類數(shù)組到數(shù)組的轉(zhuǎn)換,轉(zhuǎn)換之后,就可以使用數(shù)組的各種方法了。

你還記得在這個(gè)操作符出來之前是如何轉(zhuǎn)換的嗎?

這個(gè)問題還是頭條的一個(gè)前端面試題。

看例子:
// ES5 時(shí)代
function bar() {
  var args = Array.prototype.slice.call(arguments);

   // 調(diào)用push 加幾個(gè)元素
  args.push(1, 2, 3);

  // 把a(bǔ)rgs 作為參數(shù)傳遞給foo
  foo.apply(null, args)

}

// ES6 時(shí)代

function foo(...args) { // 搜集參數(shù)到 args

  args.push(4, 5, 6)

  console.log(...args) // 展開args

}


bar(0); // 0 1 2 3 4 5 6


基礎(chǔ)用法 4: 增加元素或?qū)傩?/h3>

1: 為數(shù)組新增成員

const pokemon = ['KK', 'Peter'];
const charmander = '鄭伊健';

const pokedex = [...pokemon, charmander];

console.log(pokedex); 

//Result: [ 'KK', 'Peter', '鄭伊健' ]


2: 為對象新增屬性

const basicSquirtle = { name: 'Squirtle', type: 'Water' };
const fullSquirtle = {
  ...basicSquirtle,
  species: 'Tiny Turtle',
  evolution: 'Wartortle'
};

console.log(fullSquirtle); 

//Result: { name: 'Squirtle', type: 'Water', species: 'Tiny Turtle', evolution: 'Wartortle' }
基礎(chǔ)用法 5: 合并數(shù)組/對象


合并數(shù)組:

const pokemon = ['Squirtle', 'Bulbasur', 'Charmander'];
const morePokemon = ['Totodile', 'Chikorita', 'Cyndaquil'];

const pokedex = [...pokemon, ...morePokemon];

console.log(pokedex); 
//Result: [ 'Squirtle', 'Bulbasur', 'Charmander', 'Totodile', 'Chikorita', 'Cyndaquil' ]



// 對象數(shù)組也一樣:
const pokemon = [
  { name: 'Squirtle', type: 'Water' },
  { name: 'Bulbasur', type: 'Plant' }
];
const morePokemon = [{ name: 'Charmander', type: 'Fire' }];

const pokedex = [...pokemon, ...morePokemon];

console.log(pokedex); 

//Result: [ { name: 'Squirtle', type: 'Water' }, { name: 'Bulbasur', type: 'Plant' }, { name: 'Charmander', type: 'Fire' } ]

合并對象
const baseSquirtle = {
  name: 'Squirtle',
  type: 'Water'
};

const squirtleDetails = {
  species: 'Tiny Turtle Pokemon',
  evolution: 'Wartortle'
};

const squirtle = { ...baseSquirtle, ...squirtleDetails };
console.log(squirtle); 
//Result: { name: 'Squirtle', type: 'Water', species: 'Tiny Turtle Pokemon', evolution: 'Wartortle' }

以上是一些基礎(chǔ)費(fèi)用法

下面介紹一些... 操作符的進(jìn)階用法。

進(jìn)階篇


1. 復(fù)制具有嵌套結(jié)構(gòu)的數(shù)據(jù)/對象

先看一個(gè)例子:
const pokemon = {
  name: 'Squirtle',
  type: 'Water',
  abilities: ['Torrent', 'Rain Dish']
};

const squirtleClone = { ...pokemon };

pokemon.name = 'Charmander';
pokemon.abilities.push('Surf');

console.log(squirtleClone); 

//Result: { name: 'Squirtle', type: 'Water', abilities: [ 'Torrent', 'Rain Dish', 'Surf' ] }
當(dāng)我們修改原對象的 name 屬性時(shí),我們的克隆對象的 name 屬性沒有受影響,這是符合我們預(yù)期的。

但是當(dāng)修改原對象的 abilities 屬性時(shí),我們的克隆對象也被修改了。

原因也很簡單,因?yàn)閺?fù)制過來的 abilities 是一個(gè)引用類型,原數(shù)據(jù)改了,用到他的地方也會(huì)跟著改。

知道原因,再解決就很簡單了,兩種方式:

1、復(fù)制引用類型的數(shù)據(jù)

const pokemon = {
  name: 'Squirtle',
  type: 'Water',
  abilities: ['Torrent', 'Rain Dish']
};

const squirtleClone = { ...pokemon, abilities: [...pokemon.abilities] };

pokemon.name = 'Charmander';
pokemon.abilities.push('Surf');

console.log(squirtleClone);

//Result: { name: 'Squirtle', type: 'Water', abilities: [ 'Torrent', 'Rain Dish' ] }
這樣就 OK 了


2、深克隆

在這里就不多解釋了。

2. 增加條件屬性

顧名思義,就是需要根據(jù)條件添加的屬性。
看個(gè)例子:
const pokemon = {
  name: 'Squirtle',
  type: 'Water'
};

const abilities = ['Torrent', 'Rain dish'];
const fullPokemon = abilities ? { ...pokemon, abilities } : pokemon;

console.log(fullPokemon);

3短路

const pokemon = {
  name: 'Squirtle',
  type: 'Water'
};

const abilities = ['Torrent', 'Rain dish'];
const fullPokemon = {
  ...pokemon,
  ...(abilities && { abilities })
};

console.log(fullPokemon);

如果 abilities 為 true,就相當(dāng)于是
const fullPokemon = {
  ...pokemon,
  ...{ abilities }
}

這也是一個(gè)很有用的技巧。

4. 默認(rèn)結(jié)構(gòu)和添加默認(rèn)屬性


默認(rèn)結(jié)構(gòu):

我們知道,當(dāng)結(jié)構(gòu)一個(gè)對象的時(shí)候,如果這個(gè)對象里沒有某個(gè)屬性,解出來是undefined , 我們可以添加默認(rèn)值來解決:

const pokemon = {
  id: 1,
  name: 'Squirtle'
};

const { type, name } = pokemon;
console.log(name); //Result: Squirtle
console.log(type); //Result: undefined

//Assigning default value to the type variable
const { type = 'Water', name } = pokemon;
console.log(type); //Result: Water


添加默認(rèn)屬性

有時(shí)候從我們會(huì)遇到這樣的情況,一個(gè)對象,大部分屬性是相似的,只有小部分是不不同的,這時(shí)候我們就可以設(shè)置一個(gè)基礎(chǔ)對象,具備基礎(chǔ)屬性,其他的對象可以通過擴(kuò)展這個(gè)對象來得到。
看例子:
const pokemon = {
  name: 'Squirtle',
  type: 'Water'
};

//  給abilities默認(rèn)賦值
const { abilities = [], ...rest } = pokemon;

const fullSquirtle = { ...rest, abilities };

console.log(rest); //Result: { name: 'Squirtle', type: 'Water' }
console.log({ fullSquirtle }); //Result: { name: 'Squirtle', type: 'Water', abilities: [] }

這里就是通過展開 rest , 合并 abilities 得到完全體的數(shù)據(jù)。
如果有批量的數(shù)據(jù)需要處理,這種方法也非常方便:
const pokemon = [
  {
    name: 'Charmander',
    type: 'Fire'
  },
  { name: 'Squirtle', type: 'Water', abilities: ['Torrent', 'Rain Dish'] },
  {
    name: 'Bulbasur',
    type: 'Plant'
  }
];

function setDefaultAbilities(object) {
  const { abilities = [], ...rest } = object;
  return { ...rest, abilities };
}

// Applying the setDefaultAbilities function to all the pokemon in the array:
const normalizedPokemon = pokemon.map(pokemon => setDefaultAbilities(pokemon));

console.log(normalizedPokemon);

//Result: [ { name: 'Charmander', type: 'Fire', abilities: [] },   { name: 'Squirtle', type: 'Water', abilities: [ 'Torrent', 'Rain Dish' ] }, { name: 'Bulbasur', type: 'Plant', abilities: [] } ]

這樣迭代一遍,所有的對象就都具備 abilities 屬性了。

總結(jié)
... 運(yùn)算符非常靈活,收放自如,非常強(qiáng)大,希望我們都能很好的掌握這個(gè)工具。

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多