【JavaScript】面試手撕淺拷貝
引入
淺拷貝和深拷貝應該是面試時非常常見的問題了,為了能將這兩者說清楚,于是打算用兩篇文章分別解釋下深淺拷貝。
PS: 我第一次聽到拷貝這個詞,有種莫名的熟悉感,感覺跟某個英文很相似,后來發現確實
Copy
的音譯,感覺這翻譯還是蠻有意思的。🐶
什么是淺拷貝
行文至此,肯定會有朋友問,什么事淺拷貝呢?顧名思義,就是淺淺的copy
一下。🐶
在JavaScript
中,對于淺拷貝分為兩種情況。
- 對于淺拷貝,如果待拷貝的數據是
基礎類型
的屬性(如Number
、String
、Boolean
等基本類型),那么只需要將這個值直接復制過來即可。 - 對于淺拷貝,如果待拷貝的數據是
引用類型
的屬性(如對象
,數組
),那么復制的則是對象的引用以及第一層的基礎屬性值。

基礎類型與引用類型的區別
談到這,順帶談一下
基礎類型
與引用類型
的區別。
基礎類型
- 直接存儲在棧內存中。
- 存儲的是實際的數據值。
- 內存空間固定且較小。
引用類型
- 對象(如數組,函數,對象等)的屬性值存在堆內存中。
- 棧內存中存儲的是指向堆內存中對象的引用或指針,而非對象本身的內容。
- 內存空間大小不固定,取決于對象結構的復雜性。

手動實現淺拷貝
因為值的拷貝可以直接賦值即可。我們這里實現下對象的淺拷貝。
對象的淺拷貝:復制對象的引用以及第一層屬性值。
思路: 我們先構造一個新對象,然后將它的屬性值等于原對象的屬性值。這里新對象和原對象的屬性依然會共享引用,但對于第一層的基礎屬性卻不會共享了。所以我們實現的淺拷貝 是拷貝一層。
function shallowClone(obj) {const newObj = {};for (let prop in obj) {if (obj.hasOwnProperty(prop)) {newObj[prop] = obj[prop];}}return newObj;
}const test = { a: 1, b: 2 };
const newTest = shallowClone(test);
newTest.a = 2;
console.log('test: ',test,' newTest: ', newTest);
/*** 輸入如下,第一層基礎屬性的值不會共享了* test: { a: 1, b: 2 } newTest: { a: 2, b: 2 }*/
Js自帶淺拷貝的方法
對于Js的淺拷貝,可以分為
對象淺拷貝
和數組淺拷貝
對象淺拷貝
對于對象的淺拷貝有object.assgin
和拓展運算符
實現,這里的淺拷貝都是拷貝一層。
Object.assign
Object.assign()
是 ES6
中引入的一個方法,用于將一個或多個源對象的所有可枚舉屬性分配到目標對象上。
const original = { a: 1, b: { c: 2 } };
const shallowCopy = Object.assign({}, original);
拓展運算符
注意:這種方式同樣會創建一個新的對象,但對嵌套對象或數組只會進行淺拷貝。
const original = { a: 1, b: { c: 2 } };
const shallowCopy = { ...original };
數組淺拷貝
slice
slice()
方法是用來從數組中提取一部分元素生成新數組的方法,它確實實現了數組的淺拷貝。
let originalArray = [1, 2, { a: 3 }, [4, 5]];
let copiedArray = originalArray.slice();// 淺拷貝后,原始數組和拷貝數組中基本類型元素是獨立的
copiedArray[0] = 100; // 修改基本類型元素,不影響原數組
console.log(originalArray); // 輸出:[1, 2, { a: 3 }, [4, 5]]
console.log(copiedArray); // 輸出:[100, 2, { a: 3 }, [4, 5]]// 但是對于引用類型元素,修改其屬性或內容會同時影響原數組和拷貝數組
copiedArray[2].a = 456;
copiedArray[3][0] = 7;console.log(originalArray); // 輸出:[1, 2, { a: 456 }, [7, 5]]
console.log(copiedArray); // 輸出:[100, 2, { a: 456 }, [7, 5]]