1. 程式人生 > >ES6擴充套件運算子

ES6擴充套件運算子

  1. 擴充套件運算(spread)
    (1). 定義
    擴充套件運算子(spread)是三個點(…)。它好比 rest 引數的逆運算,將一個數組轉為用逗號分隔的引數序列。主要用於函式的呼叫中。
function push(array, ...items) {
  array.push(...items);
}

function add(x, y) {
  return x + y;
}

const numbers = [4, 38];
add(...numbers) // 42

和正常函式的藏書結合使用,也可以放置表示式。如果擴充套件運算子後面是一個空陣列,則不產生任何效果。

function f(v, w, x, y, z) { }
const args = [0, 1];
f(-1, ...args, 2, ...[3]);
//array.push(...items)和add(...numbers)這兩行,都是函式的呼叫,它們的都使用了擴充套件運算子。該運算子將一個數組,變為引數序列。

//放置表示式
const arr = [
  ...(x > 0 ? ['a'] : []),
  'b',
];

//空陣列

[...[], 1]
// [1]

// 擴充套件運算子所在的括號不是函式呼叫,而console.log(...[1, 2])就不會報錯,因為這時是函式調
(...[1,2])
// Uncaught SyntaxError: Unexpected number
console.log((...[1,2]))
// Uncaught SyntaxError: Unexpected number

(2).替換函式 的apply用法
擴充套件運算子可以展開陣列,將陣列轉換為函式的引數。所以不需要apply()方法。

// ES5 的寫法
    console.log('ES5方式:' + Math.max.apply(null, [14, 3, 77]))

  // ES6 的寫法
    console.log('ES6方式:' + Math.max(...[14, 3, 77]))
  // 等同於
    console.log('等價ES6:' + Math.max(14, 3, 77))

(3). 擴充套件運算子的應用
a,複製陣列
b,合併陣列
c,與解構的賦值組合,用於生成陣列,語法:[a, …rest] = list,注意:如果將擴充套件運算子用於陣列賦值,只能放在引數的最後一位,否則會報錯
d,把字串轉為真正的陣列
e,實現了 Iterator 介面的物件
任何 Iterator 介面的物件,都可以用擴充套件運算子轉為真正的陣列。
f,Map 和 Set 結構,Generator 函式
擴充套件運算子內部呼叫的是資料結構的 Iterator 介面,因此只要具有 Iterator 介面的物件,都可以使用擴充套件運算子,比如 Map 結構。

//   陣列是複合的資料型別,直接複製的話,只是複製了指向底層資料結構的指標,而不是克隆一個全新的陣列
    const a1 = [1,1];
    const a2 = a1;

    console.log(a2[0] = 2  // 2
    console.log(a1)  //2,1
    console.log(a2) //2,1

//合併陣列
  const arr1 = ['a', 'b'];
    const arr2 = ['c'];
    const arr3 = ['d', 'e'];

// ES5 的合併陣列
   console.log(arr1.concat(arr2, arr3)) ;
// [ 'a', 'b', 'c', 'd', 'e' ]

// ES6 的合併陣列
    console.log([...arr1, ...arr2, ...arr3])


//-------------------------與解構的賦值組合生成陣列
// 與解構賦值合併生成陣列
const [f1, ...f2] = [1, 2, 3, 4, 5, 6];
    console.log(f1);
    console.log(...f2);
    
//將擴充套件運算子用於陣列賦值,只能放在引數的最後一位
    // 報錯
    const [...butLast, last] = [1, 2, 3, 4, 5];
   // 報錯
    const [first, ...middle, last] = [1, 2, 3, 4, 5];

//------------------------------把字串裝為陣列
console.log([...'hello_world'])

//凡是涉及到操作四個位元組的 Unicode 字元的函式,都有這個問題。因此,最好都用擴充套件運算子改寫。
function length(str) {
  return [...str].length;
}

length('x\uD83D\uDE80y') // 3

//凡是涉及到操作四個位元組的 Unicode 字元的函式,都有這個問題。因此,最好都用擴充套件運算子改寫。
let str = 'x\uD83D\uDE80y';

str.split('').reverse().join('')
// 'y\uDE80\uD83Dx'

[...str].reverse().join('')
// 'y\uD83D\uDE80x'


//------------------------------------------任何 Iterator 介面的物件,都可以用擴充套件運算子轉為真正的陣列

//querySelectorAll方法返回的是一個NodeList物件。它不是陣列,而是一個類似陣列的物件。這時,擴充套件運算子可以將其轉為真正的陣列,原因就在於NodeList物件實現了 Iterator 。
let nodeList = document.querySelectorAll('div');
let array = [...nodeList];

//對於那些沒有部署 Iterator 介面的類似陣列的物件,擴充套件運算子就無法將其轉為真正的陣列。
let arrayLike = {
  '0': 'a',
  '1': 'b',
  '2': 'c',
  length: 3
};

// TypeError: Cannot spread non-iterable object.
let arr = [...arrayLike];
//arrayLike是一個類似陣列的物件,但是沒有部署 Iterator 介面,擴充套件運算子就會報錯。這時,可以改為使用Array.from方法將arrayLike轉為真正的陣列。
let arr = Array.from(arrLike)
console.log(arr);
//Array.from方法用於將兩類物件轉為真正的陣列:類似陣列的物件(array-like object)和可遍歷(iterable)的物件(包括 ES6 新增的資料結構 Set 和 Map)

//只要具有iterate介面的物件,都可使用擴充套件運算子
    let map = new Map([
        [1,'no.1'],
        [2,'no.2'],
      ]);
  let arrMap = [...map];
    console.log(arrMap)
    console.log(...map.keys())
    console.log(...map.values())

    //generate函式執行後,返回一個遍歷器的物件,也可使用擴充套件運算子
    const ge = function*() {
        yield 1;
        yield 2;
        yield 3;
    };
    console.log([...ge()]);
    //變數ge是一個 Generator 函式,執行後返回的是一個遍歷器物件,對這個遍歷器物件執行擴充套件運算子,就會將內部遍歷得到的值,轉為一個數組。
  1. Array.from()
    

    用於將兩類物件轉為真正的陣列:類似陣列的物件(array-like object)和可遍歷(iterable)的物件(包括 ES6 新增的資料結構 Set 和 Map)
    用於將兩類物件轉為真正的陣列:類似陣列的物件和可遍歷的物件(包含ES6新增的Set和Map)
    常見的類似陣列的物件是DOM操作後返回的NodeList集合,以及函式內部的argunments物件物件
    只要部署了Iterator介面的資料結構,Array.from()都能將其轉化為真正的陣列
    Arrry.from主要解決沒有部署iterate介面,擴充套件運算子 … 就不能轉化為陣列的問題

    擴充套件運算子背後呼叫的是遍歷器介面(Symbol.iterator),如果一個物件沒有部署這個介面,就無法轉換。
    Array.from方法還支援類似陣列的物件。所謂類似陣列的物件,本質特徵只有一點,即必須有length屬性。
    因此,任何有length屬性的物件,都可以通過Array.from方法轉為陣列,而此時擴充套件運算子就無法轉換。
    可以接收第二個引數,作用類似於陣列的map方法,用於對每個元素進行處理,將處理後的值放入返回的陣列。
    返回各種資料型別

總結:
1,只要有一個原始的資料結構,你就可以先對它的值進行處理,然後轉成規範的陣列結構,進而就可以使用數量眾多的陣列方法。
2,rray.from()的另一個應用是,將字串轉為陣列,然後返回字串的長度。
因為它能正確處理各種 Unicode 字元,可以避免 JavaScript 將大於\uFFFF的 Unicode 字元,算作兩個字元的 bug。

let arrLike = {
      '0' : 'a1',
      '1' : 'a2',
      '2' : 'a3',
      'length' : 3,
    };
    var arrEs5 = [].slice.call(arrLike);
    let arrEs6 = Array.from(arrLike);
    console.log("es5:"+arrEs5)
    console.log("es6:"+arrEs6)

    //NodeList物件
    let nlObject = document.querySelectorAll('hello');//返回類似陣列的物件(array-like object)
    let nls = Array.from(nlObject).filter( p => {
      return p.textContent.length > 100;
    });
    console.log(nls)

    //函式的arguments物件
    function argsObject() {
      var args = Array.from(arguments);
      console.log(args)
    }

    //部署了Iterator介面
    console.log(Array.from('hello,小明!'))
    let namesSet = new Set(['set1','set2','set3'])
    console.log(Array.from(namesSet))

    //只要有length屬性的物件,都可以通過array.from方法轉化為陣列
    //Array.from返回了一個具有2個成員的陣列,每個位置的值都是undefined。擴充套件運算子轉換不了這個物件
    console.log(Array.from({length:2}))
    //對於還沒有部署該方法的瀏覽器,可以用Array.prototype.slice方法替代。
    const toArray = (() =>
        Array.from ? Array.from : obj => [].slice.call(obj)
    )();
    console.log(toArray)

//====================== 接收第二個引數,類似map方法
    //Array.from(arrayLike, x => x * x);
// 等同於
    //Array.from(arrayLike).map(x => x * x);

    //Array.from可以接受第二個引數,作用類似於陣列的map方法,用來對每個元素進行處理,將處理後的值放入返回的陣列。
    let arrMap = Array.from([1, 2, 3], (x) => x * x)
    console.log(arrMap)// [1, 4, 9]

    //取出一組DOM節點的文字內容
    let spans = document.querySelectorAll('span.name');
    // map()
    let names1 = Array.prototype.map.call(spans, s => s.textContent);
    console.log(names1)
    // Array.from()
    let names2 = Array.from(spans, s => s.textContent)
    console.log(names2)


    //返回資料型別
    function typesOf () {
      return Array.from(arguments, value => typeof value)
    }
    console.log(typesOf(null, [], NaN)) //["object", "object", "number"]


    //第一個引數指定第二個引數的執行次數
    console.log(Array.from({length:3},() => 'hello'))//["hello", "hello", "hello"]

    //將字串轉為陣列,然後返回字串的長度
    //理由:能夠正確的處理各種Unicode字元,可以避免JS將大於\uFFFF的Unicode字元算作兩個字元的bug。
    function countSymbols(string) {
      return Array.from(string).length;
    }
      console.log(countSymbols(''))//0
  1. Array.of
    Array.of 將一組值轉換為陣列
    該方法主要用於彌補陣列建構函式Array()的不足,因為引數的不同,會導致Array()的行為有差異
    只有引數不少於2個是,才會返回一個新的陣列,如果只有一個時,其實是指定了陣列的長度
    可以用來替換Array()或new Array(),並且不會因引數不同而導致過載。
    總是返回引數值組成的陣列,如果沒有引數,就返回一個空陣列。
//把一組值轉化為陣列
    console.log(Array.of(1,2,3,4))//(4) [1, 2, 3, 4]
    console.log(Array.of(2).length)//1
    console.log(Array.of().length)//0
//只有當引數個數不少於 2 個時,Array()才會返回由引數組成的新陣列。引數個數只有一個時,實際上是指定陣列的長度。

    console.log(Array.of()) // []
    console.log( Array.of(undefined)) // [undefined]
    console.log(Array.of(1)) // [1]
    console.log(Array.of(1, 2)) // [1, 2]

  1. 陣列例項的 copyWithin()
    陣列例項的copyWithin方法,在當前陣列內部,將指定位置的成員複製到其他位置(會覆蓋原有成員),然後返回當前陣列。也就是說,使用這個方法,會修改當前陣列。
Array.prototype.copyWithin(target, start = 0, end = this.length)
它接受三個引數。
target(必需):從該位置開始替換資料。如果為負值,表示倒數。
start(可選):從該位置開始讀取資料,預設為 0。如果為負值,表示倒數。
end(可選):到該位置前停止讀取資料,預設等於陣列長度。如果為負值,表示倒數。
這三個引數都應該是數值,如果不是,會自動轉為數值。
console.log([1, 2, 3, 4, 5].copyWithin(0, 3))//將從 3 號位直到陣列結束的成員(4 和 5),複製到從 0 號位開始的位置,結果覆蓋了原來的 1 和 2。
// [4, 5, 3, 4, 5]
// 將3號位複製到0號位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]

// -2相當於3號位,-1相當於4號位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]

// 將3號位複製到0號位
[].copyWithin.call({length: 5, 3: 1}, 0, 3)
// {0: 1, 3: 1, length: 5}

// 將2號位到陣列結束,複製到0號位
let i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]

// 對於沒有部署 TypedArray 的 copyWithin 方法的平臺
// 需要採用下面的寫法
[].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
// Int32Array [4, 2, 3, 4, 5]
  1. 陣列例項的 find() 和 findIndex()
    find:
    陣列例項的find方法,用於找出第一個符合條件的陣列成員。它的引數是一個回撥函式,所有陣列成員依次執行該回調函式,直到找出第一個返回值為true的成員,然後返回該成員。如果沒有符合條件的成員,則返回undefined。
[1, 4, -5, 10].find((n) => n < 0)
// -5

find方法的回撥函式可以接受三個引數,依次為當前的值、當前的位置和原陣列。

[1, 5, 10, 15].find(function(value, index, arr) {
  return value > 9;
}) // 10

陣列例項的findIndex:
返回第一個符合條件的陣列成員的位置,如果所有成員都不符合條件,則返回-1。

[1, 5, 10, 15].findIndex(function(value, index, arr) {
  return value > 9;
}) // 2

 function f(v){
      return v > this.age;
    }
    let person = {name: 'John', age: 20};
    console.log([10, 12, 26, 15].find(f, person));    // 26  find函式接收了第二個引數person物件,回撥函式中的this物件指向person物件。

總結:
兩個方法都可以接受第二個引數,用來繫結回撥函式的this物件。
都可以發現NaN,彌補了陣列的indexOf方法的不足。

[NaN].indexOf(NaN)
// -1

[NaN].findIndex(y => Object.is(NaN, y))
// 0
  1. 陣列例項的 fill()
    fill方法使用給定值,填充一個數組。
    fill方法用於空陣列的初始化非常方便。陣列中已有的元素,會被全部抹去。
    fill方法還可以接受第二個和第三個引數,用於指定填充的起始位置和結束位置
    注意:如果填充物件的型別為物件,那麼被賦值的是同一個記憶體地址的物件,而不是深拷貝物件
['a', 'b', 'c'].fill(7)
// [7, 7, 7]

new Array(3).fill(7)
// [7, 7, 7]

//指定起始位置
['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']

 //填充物件
 let arr = new Array(3).fill({name: "Mike"});
arr[0].name = "Ben";
 arr
// [{name: "Ben"}, {name: "Ben"}, {name: "Ben"}]

let arr2 = new Array(3).fill([]);
arr2[0].push(5);
arr2
// [[5], [5], [5]]
  1. 陣列例項的 entries(),keys() 和 values()
    ES6 提供三個新的方法——entries(),keys()和values()——用於遍歷陣列。它們都返回一個遍歷器物件(詳見《Iterator》一章),可以用for…of迴圈進行遍歷,唯一的區別是keys()是對鍵名的遍歷、values()是對鍵值的遍歷,entries()是對鍵值對的遍歷。
    如果不使用for…of迴圈,可以手動呼叫遍歷器物件的next方法,進行遍歷.
for (let index of ['a', 'b'].keys()) {
  console.log(index);
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"

//用next進行遍歷
let letter = ['a', 'b', 'c'];
let entries = letter.entries();
console.log(entries.next().value); // [0, 'a']
console.log(entries.next().value); // [1, 'b']
console.log(entries.next().value); // [2, 'c']
  1. 陣列例項的 includes()
    Array.prototype.includes方法返回一個布林值,表示某個陣列是否包含給定的值,與字串的includes方法類似。ES2016 引入了該方法。
    該方法的第二個引數表示搜尋的起始位置,預設為0。如果第二個引數為負數,則表示倒數的位置,如果這時它大於陣列長度(比如第二個引數為-4,但陣列長度為3),則會重置為從0開始。

  2. Map 和 Set 資料結構有一個has方法,與includes區別。
    Map 結構的has方法,是用來查詢鍵名的,比如Map.prototype.has(key)、WeakMap.prototype.has(key)、Reflect.has(target, propertyKey)。
    Set 結構的has方法,是用來查詢值的,比如Set.prototype.has(value)、WeakSet.prototype.has(value)。

以前通常使用陣列的indexOf方法,檢查是否包含某個值的缺點:
一是不夠語義化,它的含義是找到引數值的第一個出現位置,所以要去比較是否不等於-1,表達起來不夠直觀。
二是,它內部使用嚴格相等運算子(===)進行判斷,這會導致對NaN的誤判。

[NaN].indexOf(NaN)
// -1
//includes使用的是不一樣的判斷演算法,就沒有這個問題。
[NaN].includes(NaN)
// true

  1. 詳細資訊參考 http://es6.ruanyifeng.com/#docs/array