JavaScript中十個一步拷貝數組的方法

十種一步拷貝數組的方法

JavaScript中咱們常常會遇到拷貝數組的場景,可是都有哪些方式可以來實現呢,咱們不妨來梳理一下。javascript

一、擴展運算符(淺拷貝)

自從ES6出現以來,這已經成爲最流行的方法。它是一個很簡單的語法,可是當你在使用相似於React和Redux這類庫時,你會發現它是很是很是有用的。java

numbers = [1, 2, 3];
numbersCopy = [...numbers];
複製代碼

這個方法不能有效的拷貝多維數組。數組/對象值的拷貝是經過引用而不是值複製。git

// 😊
numbersCopy.push(4);
console.log(numbers, numbersCopy);
// [1, 2, 3] and [1, 2, 3, 4]
// 只修改了咱們但願修改的,原數組不受影響

// 😢
nestedNumbers = [[1], [2]];
numbersCopy = [...nestedNumbers];
numbersCopy[0].push(300);
console.log(nestedNumbers, numbersCopy);
// [[1, 300], [2]]
// [[1, 300], [2]]
// 因爲公用引用,因此兩個數組都被修改了,這是咱們不但願的
複製代碼

二、for()循環(淺拷貝)

考慮到函數式編程變得愈來愈流行,我認爲這種方法多是最不受歡迎的。github

numbers = [1, 2, 3];
numbersCopy = [];
for (i = 0; i < numbers.length; i++) {
  numbersCopy[i] = numbers[i];
}

複製代碼

這個方法不能有效的拷貝多維數組。由於咱們使用的是=運算符,它在處理數組/對象值的拷貝時經過引用而不是值複製。編程

// 😊
numbersCopy.push(4);
console.log(numbers, numbersCopy);
// [1, 2, 3] and [1, 2, 3, 4]

// 😢
nestedNumbers = [[1], [2]];
numbersCopy = [];
for (i = 0; i < nestedNumbers.length; i++) {
  numbersCopy[i] = nestedNumbers[i];
}
numbersCopy[0].push(300);
console.log(nestedNumbers, numbersCopy);
// [[1, 300], [2]]
// [[1, 300], [2]]
// 因爲公用引用,因此兩個數組都被修改了,這是咱們不但願的

複製代碼

三、while()循環(淺拷貝)

for() 相似。數組

numbers = [1, 2, 3];
numbersCopy = [];
i = -1;
while (++i < numbers.length) {
  numbersCopy[i] = numbers[i];
}
複製代碼

四、Array.map(淺拷貝)

上面的forwhile都是很「古老」的方式,讓咱們繼續回到當前,咱們會發現map方法。map源於數學,是將一個集合轉換成另外一種集合,同時保留結構的概念。安全

在英語中,它意味着Array.map 每次返回相同長度的數組。微信

numbers = [1, 2, 3];
double = (x) => x * 2;

numbers.map(double);
複製代碼

當咱們使用map方法時,須要給出一個callback函數用於處理當前的數組,並返回一個新的數組元素。數據結構

和拷貝數組有什麼關係呢?ide

當咱們想要複製一個數組的時候,只須要在mapcallback函數中直接返回原數組的元素便可。

numbers = [1, 2, 3];
numbersCopy = numbers.map((x) => x);
複製代碼

若是你想更數學化一點,(x) => x叫作恆等式。它返回給定的任何參數。

identity = (x) => x;
numbers.map(identity);
// [1, 2, 3]
複製代碼

一樣的,處理對象和數組的時候是引用而不是值複製。

五、Array.filter(淺拷貝)

Array.filter方法一樣會返回一個新數組,可是並不必定是返回一樣長度的,這和咱們的過濾條件有關。

[1, 2, 3].filter((x) => x % 2 === 0)
// [2]
複製代碼

當咱們的過濾條件老是true時,就能夠用來實現拷貝。

numbers = [1, 2, 3];
numbersCopy = numbers.filter(() => true);
// [1, 2, 3]
複製代碼

一樣的,處理對象和數組的時候是引用而不是值複製。

六、Array.reduce(淺拷貝)

其實用reduce來拷貝數組並無展現出它的實際功能,可是咱們仍是要將其可以拷貝數組的能力說一下的

numbers = [1, 2, 3];
numbersCopy = numbers.reduce((newArray, element) => {
  newArray.push(element);
  return newArray;
}, []);
複製代碼

reduce() 方法對數組中的每一個元素執行一個由您提供的reducer函數,將其結果彙總爲單個返回值。

上面咱們的例子中初始值是一個空數組,咱們在遍歷原數組的時候來填充這個空數組。該數組必需要從下一個迭代函數的執行後被返回出來。

一樣的,處理對象和數組的時候是引用而不是值複製。

七、Array.slice(淺拷貝)

slice 方法根據咱們指定的start、end的index從原數組中返回一個淺拷貝的數組。

[1, 2, 3, 4, 5].slice(0, 3);
// [1, 2, 3]
// Starts at index 0, stops at index 3

// 當不給定參數時,就返回了原數組的拷貝
numbers = [1, 2, 3, 4, 5];
numbersCopy = numbers.slice();
// [1, 2, 3, 4, 5]
複製代碼

一樣的,處理對象和數組的時候是引用而不是值複製。

八、JSON.parse & JSON.stringify(深拷貝)

JSON.stringify將一個對象轉成字符串; JSON.parse將轉成的字符串轉回對象。

將它們組合起來能夠將對象轉換成字符串,而後反轉這個過程來建立一個全新的數據結構。

nestedNumbers = [[1], [2]];
numbersCopy = JSON.parse(
  JSON.stringify(nestedNumbers)
);
numbersCopy[0].push(300);
console.log(nestedNumbers, numbersCopy);
// [[1], [2]]
// [[1, 300], [2]]
// These two arrays are completely separate!
複製代碼

這個能夠安全地拷貝深度嵌套的對象/數組

幾種特殊狀況

一、若是obj裏面有時間對象,則JSON.stringify後再JSON.parse的結果,時間將只是字符串的形式。而不是時間對象;

var test = {
  name: 'a',
  date: [new Date(1536627600000), new Date(1540047600000)],
};

let b;
b = JSON.parse(JSON.stringify(test))
console.log(b)
複製代碼

二、若是obj裏有RegExp、Error對象,則序列化的結果將只獲得空對象;

const test = {
  name: 'a',
  date: new RegExp('\\w+'),
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = 'test'
console.log('ddd', test, copyed)
複製代碼

三、若是obj裏有函數,undefined,則序列化的結果會把函數或 undefined丟失;

const test = {
  name: 'a',
  date: function hehe() {
    console.log('fff')
  },
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = 'test'
console.error('ddd', test, copyed)
複製代碼

四、若是obj裏有NaN、Infinity和-Infinity,則序列化的結果會變成null

五、JSON.stringify()只能序列化對象的可枚舉的自有屬性,例如 若是obj中的對象是有構造函數生成的, 則使用JSON.parse(JSON.stringify(obj))深拷貝後,會丟棄對象的constructor;

function Person(name) {
  this.name = name;
  console.log(name)
}

const liai = new Person('liai');

const test = {
  name: 'a',
  date: liai,
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = 'test'
console.error('ddd', test, copyed)
複製代碼

參考文章:關於JSON.parse(JSON.stringify(obj))實現深拷貝應該注意的坑

九、Array.concat(淺拷貝)

concat將數組與值或其餘數組進行組合。

[1, 2, 3].concat(4); // [1, 2, 3, 4]
[1, 2, 3].concat([4, 5]); // [1, 2, 3, 4, 5]
複製代碼

若是咱們不指定參數或者提供一個空數組做爲參數,就能夠進行淺拷貝。

[1, 2, 3].concat(); // [1, 2, 3]
[1, 2, 3].concat([]); // [1, 2, 3]
複製代碼

一樣的,處理對象和數組的時候是引用而不是值複製。

十、Array.from(淺拷貝)

能夠將任何可迭代對象轉換爲數組。給一個數組返回一個淺拷貝。

console.log(Array.from('foo'))
// ['f', 'o', 'o']

numbers = [1, 2, 3];
numbersCopy = Array.from(numbers)
// [1, 2, 3]
複製代碼

一樣的,處理對象和數組的時候是引用而不是值複製。


小結

上面這些方法都是在使用一個步驟來進行拷貝。若是咱們結合一些其餘的方法或技術可以發現還有不少的方式來實現數組的拷貝,好比一系列的拷貝工具函數等。


原文地址: How to clone an array in JavaScript


關注微信公衆號同步推送更新

圖片描述

或者能夠去Github上面給個Star

相關文章
相關標籤/搜索