JavaScript中十種一步拷貝數(shù)組的方法實(shí)例詳解
JavaScript中我們經(jīng)常會(huì)遇到拷貝數(shù)組的場景,但是都有哪些方式能夠來實(shí)現(xiàn)呢,我們不妨來梳理一下。

1、擴(kuò)展運(yùn)算符(淺拷貝)
自從ES6出現(xiàn)以來,這已經(jīng)成為最流行的方法。它是一個(gè)很簡單的語法,但是當(dāng)你在使用類似于React和Redux這類庫時(shí),你會(huì)發(fā)現(xiàn)它是非常非常有用的。
numbers = [1, 2, 3]; numbersCopy = [...numbers]; 這個(gè)方法不能有效的拷貝多維數(shù)組。數(shù)組/對象值的拷貝是通過引用而不是值復(fù)制。 // numbersCopy.push(4); console.log(numbers, numbersCopy); // [1, 2, 3] and [1, 2, 3, 4] // 只修改了我們希望修改的,原數(shù)組不受影響 // nestedNumbers = [[1], [2]]; numbersCopy = [...nestedNumbers]; numbersCopy[0].push(300); console.log(nestedNumbers, numbersCopy); // [[1, 300], [2]] // [[1, 300], [2]] // 由于公用引用,所以兩個(gè)數(shù)組都被修改了,這是我們不希望的
2、for()循環(huán)(淺拷貝)
考慮到函數(shù)式編程變得越來越流行,我認(rèn)為這種方法可能是最不受歡迎的。
numbers = [1, 2, 3];
numbersCopy = [];
for (i = 0; i < numbers.length; i++) {
numbersCopy[i] = numbers[i];
}
這個(gè)方法不能有效的拷貝多維數(shù)組。因?yàn)槲覀兪褂玫氖?運(yùn)算符,它在處理數(shù)組/對象值的拷貝時(shí)通過引用而不是值復(fù)制。
// 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]]
// 由于公用引用,所以兩個(gè)數(shù)組都被修改了,這是我們不希望的
3、while()循環(huán)(淺拷貝)和for() 類似。
numbers = [1, 2, 3];
numbersCopy = [];
i = -1;
while (++i < numbers.length) {
numbersCopy[i] = numbers[i];
}
4、Array.map(淺拷貝)
上面的for和while都是很“古老”的方式,讓我們繼續(xù)回到當(dāng)前,我們會(huì)發(fā)現(xiàn)map方法。map源于數(shù)學(xué),是將一個(gè)集合轉(zhuǎn)換成另一種集合,同時(shí)保留結(jié)構(gòu)的概念。
在英語中,它意味著Array.map 每次返回相同長度的數(shù)組。
numbers = [1, 2, 3]; double = (x) => x * 2; numbers.map(double);
當(dāng)我們使用map方法時(shí),需要給出一個(gè)callback函數(shù)用于處理當(dāng)前的數(shù)組,并返回一個(gè)新的數(shù)組元素。
和拷貝數(shù)組有什么關(guān)系呢?
當(dāng)我們想要復(fù)制一個(gè)數(shù)組的時(shí)候,只需要在map的callback函數(shù)中直接返回原數(shù)組的元素即可。
numbers = [1, 2, 3]; numbersCopy = numbers.map((x) => x);
如果你想更數(shù)學(xué)化一點(diǎn),(x) => x叫做恒等式。它返回給定的任何參數(shù)。
identity = (x) => x; numbers.map(identity); // [1, 2, 3]
同樣的,處理對象和數(shù)組的時(shí)候是引用而不是值復(fù)制。
5、Array.filter(淺拷貝)
Array.filter方法同樣會(huì)返回一個(gè)新數(shù)組,但是并不一定是返回同樣長度的,這和我們的過濾條件有關(guān)。
[1, 2, 3].filter((x) => x % 2 === 0) // [2]
當(dāng)我們的過濾條件總是true時(shí),就可以用來實(shí)現(xiàn)拷貝。
numbers = [1, 2, 3]; numbersCopy = numbers.filter(() => true); // [1, 2, 3]
同樣的,處理對象和數(shù)組的時(shí)候是引用而不是值復(fù)制。
6、Array.reduce(淺拷貝)
其實(shí)用reduce來拷貝數(shù)組并沒有展示出它的實(shí)際功能,但是我們還是要將其能夠拷貝數(shù)組的能力說一下的
numbers = [1, 2, 3];
numbersCopy = numbers.reduce((newArray, element) => {
newArray.push(element);
return newArray;
}, []);
reduce() 方法對數(shù)組中的每個(gè)元素執(zhí)行一個(gè)由您提供的reducer函數(shù),將其結(jié)果匯總為單個(gè)返回值。
上面我們的例子中初始值是一個(gè)空數(shù)組,我們在遍歷原數(shù)組的時(shí)候來填充這個(gè)空數(shù)組。該數(shù)組必須要從下一個(gè)迭代函數(shù)的執(zhí)行后被返回出來。
同樣的,處理對象和數(shù)組的時(shí)候是引用而不是值復(fù)制。
7、Array.slice(淺拷貝)
slice 方法根據(jù)我們指定的start、end的index從原數(shù)組中返回一個(gè)淺拷貝的數(shù)組。
[1, 2, 3, 4, 5].slice(0, 3); // [1, 2, 3] // Starts at index 0, stops at index 3 // 當(dāng)不給定參數(shù)時(shí),就返回了原數(shù)組的拷貝 numbers = [1, 2, 3, 4, 5]; numbersCopy = numbers.slice(); // [1, 2, 3, 4, 5]
同樣的,處理對象和數(shù)組的時(shí)候是引用而不是值復(fù)制。
8、JSON.parse & JSON.stringify(深拷貝)
JSON.stringify將一個(gè)對象轉(zhuǎn)成字符串;
JSON.parse將轉(zhuǎn)成的字符串轉(zhuǎn)回對象。
將它們組合起來可以將對象轉(zhuǎn)換成字符串,然后反轉(zhuǎn)這個(gè)過程來創(chuàng)建一個(gè)全新的數(shù)據(jù)結(jié)構(gòu)。
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!
這個(gè)可以安全地拷貝深度嵌套的對象/數(shù)組
幾種特殊情況
1、如果obj里面有時(shí)間對象,則JSON.stringify后再JSON.parse的結(jié)果,時(shí)間將只是字符串的形式。而不是時(shí)間對象;
var test = {
name: 'a',
date: [new Date(1536627600000), new Date(1540047600000)],
};
let b;
b = JSON.parse(JSON.stringify(test))
console.log(b)
2、如果obj里有RegExp、Error對象,則序列化的結(jié)果將只得到空對象;
const test = {
name: 'a',
date: new RegExp('\\w+'),
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = 'test'
console.log('ddd', test, copyed)
3、如果obj里有函數(shù),undefined,則序列化的結(jié)果會(huì)把函數(shù)或 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)
4、如果obj里有NaN、Infinity和-Infinity,則序列化的結(jié)果會(huì)變成null
5、JSON.stringify()只能序列化對象的可枚舉的自有屬性,例如 如果obj中的對象是有構(gòu)造函數(shù)生成的, 則使用JSON.parse(JSON.stringify(obj))深拷貝后,會(huì)丟棄對象的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)
9、Array.cancat(淺拷貝)
concat將數(shù)組與值或其他數(shù)組進(jìn)行組合。
[1, 2, 3].concat(4); // [1, 2, 3, 4] [1, 2, 3].concat([4, 5]); // [1, 2, 3, 4, 5]
如果我們不指定參數(shù)或者提供一個(gè)空數(shù)組作為參數(shù),就可以進(jìn)行淺拷貝。
[1, 2, 3].concat(); // [1, 2, 3] [1, 2, 3].concat([]); // [1, 2, 3]
同樣的,處理對象和數(shù)組的時(shí)候是引用而不是值復(fù)制。
10、Array.from(淺拷貝)
可以將任何可迭代對象轉(zhuǎn)換為數(shù)組。給一個(gè)數(shù)組返回一個(gè)淺拷貝。
console.log(Array.from('foo'))
// ['f', 'o', 'o']
numbers = [1, 2, 3];
numbersCopy = Array.from(numbers)
// [1, 2, 3]
同樣的,處理對象和數(shù)組的時(shí)候是引用而不是值復(fù)制。小結(jié)
上面這些方法都是在使用一個(gè)步驟來進(jìn)行拷貝。如果我們結(jié)合一些其他的方法或技術(shù)能夠發(fā)現(xiàn)還有很多的方式來實(shí)現(xiàn)數(shù)組的拷貝,比如一系列的拷貝工具函數(shù)等。
以上所述是小編給大家介紹的JavaScript中十種一步拷貝數(shù)組的方法實(shí)例詳解,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會(huì)及時(shí)回復(fù)大家的!
相關(guān)文章
利用javascript實(shí)現(xiàn)一些常用軟件的下載導(dǎo)航
利用javascript實(shí)現(xiàn)一些常用軟件的下載導(dǎo)航,非常不錯(cuò)的應(yīng)用,思路值得借鑒,沒看過的朋友可以看下。2009-08-08
javascript面向?qū)ο笾蚕沓蓡T屬性與方法及prototype關(guān)鍵字用法
這篇文章主要介紹了javascript面向?qū)ο笾蚕沓蓡T屬性與方法及prototype關(guān)鍵字用法,實(shí)例分析了prototype關(guān)鍵字在共享成員屬性與方法中的原理與使用技巧,需要的朋友可以參考下2015-01-01
CKEditor擴(kuò)展插件:自動(dòng)排版功能autoformat插件實(shí)現(xiàn)方法詳解
這篇文章主要介紹了CKEditor擴(kuò)展插件:自動(dòng)排版功能autoformat插件實(shí)現(xiàn)方法,結(jié)合實(shí)例形式詳細(xì)分析了CKEditor擴(kuò)展插件實(shí)現(xiàn)自動(dòng)排版功能的autoformat插件具體定義、配置與使用技巧,需要的朋友可以參考下2020-02-02
BootStrap Validator 版本差異問題導(dǎo)致的submitHandler失效問題的解決方法
這篇文章主要介紹了BootStrap Validator 版本差異問題導(dǎo)致的submitHandler失效問題的解決方法,下面通過本文給大家詳細(xì)說明一下,需要的朋友可以參考下2016-12-12
javascript計(jì)算當(dāng)月剩余天數(shù)(天數(shù)計(jì)算器)示例代碼
本文介紹了利用Javascript在網(wǎng)頁上計(jì)算當(dāng)前月份的剩余天數(shù)的方法,大家參考使用吧2014-01-01
JavaScript數(shù)組扁平轉(zhuǎn)樹形結(jié)構(gòu)數(shù)據(jù)(Tree)的實(shí)現(xiàn)
本文主要介紹了JavaScript數(shù)組扁平轉(zhuǎn)樹形結(jié)構(gòu)數(shù)據(jù)(Tree)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08

