在開發過程當中,偶爾會遇到這種場景,拿到一個數據後,你打算對它進行處理,可是你又但願拷貝一份副本出來,方便數據對比和之後恢復數據。javascript
那麼這就涉及到了 JS 中對數據的深淺拷貝問題,所謂深淺拷貝,淺拷貝的意思就是,你只是複製了對象數據的引用,並無把內存裏的值另外複製一份,那麼深拷貝就是把值完整地複製一份新的值。java
在以前的文章《JS專題之數據類型和類型檢測》中我有講過,JS 中的數據類型分爲兩種,基本數據類型和引用數據類型,基本數據類型是保存在棧的數據結構中的,是按值訪問,因此不存在深淺拷貝問題。jquery
而好比對象,數組,函數,正則,時間對象這些都是引用數據類型,是保存在堆中的。因此,引用數據類型的複製,是內存地址的傳遞,並無拷貝出一份新的數據。數組
那麼深拷貝,淺拷貝的區別是什麼呢?先給結論:數據結構
操做拷貝以後的數據不會影響到原數據的值拷貝,就是深拷貝,反正,有影響則爲淺拷貝。閉包
平常開發中,JS 拷貝大多會在 數據保存,數據比對,數據同步 時出現,因此,當你在這些場景的時候,要記得裏面隱藏有一個數據深淺拷貝的問題。app
咱們來看一下淺拷貝:函數
function clone(origin) {
var result = {};
for (var prop in origin) {
if (origin.hasOwnProperty(prop)) {
result[prop] = origin[prop];
}
}
return result;
}
var jay = {
name: "jayChou",
age: 40,
family: {
wife: "Quinlivan"
}
}
var otherJay = clone(jay);
otherJay.age = 18;
otherJay.family.wife = "otherGirl";
console.log(jay);
//
// {
// name: "jayChou",
// age: 40, // 沒被改變
// family: {
// wife: "otherGirl" // 同時被改變,說明是同一個引用
// }
// }
console.log(otherJay);
//
// {
// name: "jayChou",
// age: 18,
// family: {
// wife: "otherGirl" // 被改變了
// }
// }
複製代碼
咱們發現,首先,淺拷貝不是直接賦值,淺拷貝新建了一個對象,而後將源對象的屬性都一一複製過來,複製的是值,而不是引用。工具
咱們知道,對象都是按地址引用進行訪問的,淺拷貝的複製只複製了第一層的屬性,並無遞歸將全部的值複製過來,因此,操做拷貝數據,對原數據產生了影響,故而爲淺拷貝。post
進而,那些能夠直接返回原數組的方法就能夠簡單實現數組和對象淺拷貝。
// 一、 數組淺拷貝 - slice
function shallowCopy1(origin) {
return origin.slice();
}
// 二、 數組淺拷貝 - concat
function shallowCopy2(origin){
return origin.concat();
}
// 三、 數組淺拷貝 - 遍歷
function shallowCopy3(origin){
var result = [];
for(var i = 0; i < origin.length; i++) {
result.push(origin[i]);
}
return result;
}
// 四、 對象淺拷貝 - Object.assign
function shallowCopy4(origin) {
return Object.assign({},origin)
}
// 五、 對象淺拷貝 - 擴展運算符
// 擴展運算符(...)用於取出參數對象的全部可遍歷屬性,拷貝到當前對象之中
function shallowCopy5(origin) {
return {
...origin
}
}
複製代碼
Object.assign 的拷貝,假如源對象的屬性值是一個指向對象的引用,它也只拷貝那個引用值。MDN 有相應的實例和解釋。
深拷貝就完整複製數據的值(而非引用),目的在於避免拷貝後數據對原數據產生影響。
目前深拷貝的實現方法主要有遞歸複製,JSON 正反序列化:
// 1. 深拷貝 - JSON 正反序列化
// 缺點就是沒法拷貝 undefined、function、symbol 這類特殊的屬性值。
function deepClone1(origin) {
return JSON.parse(JSON.stringify(arr));
}
// 2. 深拷貝 - 遞歸;
function deepClone2(origin) {
const result = origin.constructor === Array ? [] : {};
for (let keys in origin) {
// 不遍歷原型鏈上的屬性
if (origin.hasOwnProperty(keys)) {
if (origin[keys] && typeof origin[keys] === "object") {
// 若是值是對象,就遞歸一下, 區分是通常對象仍是數組對象
result[keys] = origin[keys].constructor === Array ? [] : {};
// 若是是引用數據類型,會遞歸調用
result[keys] = deepClone(origin[keys]);
} else {
// 若是不是,就直接賦值
result[keys] = origin[keys];
}
}
}
return result;
}
複製代碼
JS 的深拷貝的應用,須要根據你的使用場景進行使用,首先是有無必要深拷貝,其次是數據類型,是否直接使用 JSON 的 API 其實就能夠。
JS 深淺拷貝在平常開發中使用頻率仍是較高的,其中考察的知識點,主要在於:
一、是否遇到過深淺拷貝的問題,裏面有什麼坑
二、是否瞭解 JS 的數據類型,數據在計算機中的存儲機制
三、是否瞭解數組、對象的一些經常使用的 API
四、jquery、lodash、underscore 的相關工具函數使用
深淺拷貝主要考察了開發者對 JS 數據類型的瞭解,數組,對象經常使用方法的特色和應用,遞歸函數的封裝。
春節快樂!
寫於大年三十,不寫文章渾身不舒服~ 歡迎關注個人我的公衆號「謝南波」,專一分享原創文章。