詳解JS變數儲存深拷貝和淺拷貝
變數型別與儲存空間
棧記憶體和堆記憶體
基本資料型別
string、number、null、undKQgoGnuefined、boolean、symbol(ES6新增) 變數值存放在棧記憶體中,可直接訪問和修改變數的值
基本資料型別不存在拷貝,好比如說你無法修改數值1的值
引用型別
Object Function RegExp Math Date 值為物件,存放在堆記憶體中
在棧記憶體中變數儲存的是一個指標,指向對應在堆記憶體中的地址。
當訪問引用型別的時候,要先從棧中取出該物件的地址指標,然後再從堆記憶體中取得所需的資料
圖解儲存空間
let a1 = 0; // 棧記憶體 let a2 = "this is string" // 棧記憶體 let a3 = null; // 棧記憶體 let b = { x: 10 }; // 變數b存在於棧中,{ x: 10 }作為物件存在於堆中 let c = [1,2,3]; // 變數c存在於棧中,[1,3]作為物件存在於堆中
引用型別的賦值
let a = { x: 10,程式設計客棧 y: 20 } let b = a; b.x = 5; console.log(a.x); // 5
深拷貝和淺拷貝
深拷貝
將一個物件從記憶體中完整的拷貝一份出來,從堆記憶體中開闢一個新的區域存放新物件,且修改新物件不會影響原物件
淺拷貝
淺拷貝是按位拷貝物件,它會建立一個新物件,這個物件有著原始物件屬性值的一份精確拷貝。如果屬性是基本型別,拷貝的就是基本型別的值;如果屬性是記憶體地址(引用型別),拷貝的就是記憶體地址
物件的賦值
當我們把一個物件賦值給一個新的變數時,賦的其實是該物件的在棧中的地址,而不是堆中的資料。也就是兩個物件指向的是同一個儲存空間,無論哪個物件發生改變,其實都是改變的儲存空間的內容,因此,兩個物件是聯動的。
三者對比
淺拷貝的常用的五種方法
Object.assign()
Object.assign() 方法可以把任意多個的源物件自身的可列舉屬性拷貝給目標物件,然後返回目標物件。但是 Object.assign()進行的是淺拷貝
Object.assign 會從左往右遍歷源物件(sources)的所有屬性,然後用 = 賦值到目標物件(target)
var obj = { a: {a: "kobe",b: 39},b:1 }; var initalObj = Object.assign({},obj); initalObj.a.a = "wade"; initalObj.b = 2; console.log(obj.a.a); //wade console.log(obj.b); //1
擴充套件運算子
let obj = {a:1,b:{c:1}}
let obj2 = {...KQgoGnuobj};
obj.a=2;
console.log(obj); //{a:2,b:{c:1}}
console.log(obj2); //{a:1,b:{c:1}}
obj.b.c = 2;
console.log(obj); //{a:2,b:{c:2}}
console.log(obj2); //{a:1,b:{c:2}}
Array.prototype.slice
slice() 方法返回一個新的陣列物件,這一物件是一個由 begin和 end(不包括end)決定的原陣列的淺拷貝。原始陣列的基本型別不會被改變,引用型別會被改變。
let arr = [1,3,{ username: ' kobe' }]; let arr3 = arr.slice(); arr3[0]=0; arr3[2].username = 'wade' console.log(arr);
Array.prototype.concawww.cppcns.comt()
let arr = [1,{ username: 'kobe' }]; let arr2=arr.concat(); arr3[0]=0; arr2[2].username = 'wade'; console.log(arr);
手寫淺拷貝
function shallowCopy(src) { var dst = {}; for (var prop in src) { if (src.hasOwnProperty(prop)) { dst[prop] = src[prop]; } } return dst; }
深拷貝的常用方法
jsON.parse(jsON.stringify())
通過JSON.stringify實現深拷貝有幾點要注意
拷貝的物件的值中如果有函式,undefined,symbol則經過JSON.stringify()序列化後的JSON字串中這個鍵值對會消失
無法拷貝不可列舉的屬性,無法拷貝物件的原型鏈
拷貝Date引用型別會變成字串
拷貝RegExp引用型別會變成空物件
物件中含有NaN、Infinity和-Infinity,則序列化的結果會變成null
無法拷貝物件的迴圈應用(即obj[key] = obj)
let arr = [1,{ username: ' kobe' }]; let arr4 = JSON.parse(JSON.stringify(arr)); arr4[2].username = 'duncan'; console.log(arr,arr4)
手寫乞丐版深拷貝
首先這個deepClone函式並不能複製不可列舉的屬性以及Symbol型別
這裡只是針對Object引用型別的值做的迴圈迭代,而對於Array,Date,RegExp,Error,Function引用型別無法正確拷貝
物件成環,即迴圈引用 (例如:obj1.a = obj)
function clone(target) { if (typeof target === 'object') { let cloneTarget = Array.isArray(target) ? [] : {}; for (const key in target) { cloneTarget[key] = clone(target[key]); } return cloneTarget; } else { return target; } };
皇帝版深拷貝
該例項來自ConardLi大佬的github,源地址:https://github.com/ConardLi/
const mapTag = "[object Map]"; const setTag = "[object Set]"; const arrayTag = "[object Array]"; const objectTag = "[object Object]"; const argsTag = "[object Arguments]"; const boolTag = "[object Boolean]"; const dateTag = "[object Date]"; const numberTag = "[object Number]"; const stringTag = "[object String]"; const symbolTag = "[object Symbol]"; const errorTag = "[object Error]"; const regexpTag = "[object RegExp]"; const funcTag = "[object Function]"; const deepTag = [mapTag,setTag,arrayTag,objectTag,argsTag]; function forEach(array,iteratee) { let index = -1; const length = array.length; while (++index < length) { iteratee(array[index],index); } return array; } function isObject(target) { const type = typeof target; return target !== null && (type === "object" || type === "function"); } function getType(target) { return Object.prototype.toString.call(target); } function getInit(target) { const Ctor = target.constructor; return new Ctor(); } function cloneSymbol(targe) { return Object(Symbol.prototype.valueOf.call(targe)); } function cloneReg(targe) { const reFlags = /\w*$/; const result = new targe.constructor(targe.source,reFlags.exec(targe)); result.lastIndex = targe.lastIndex; return result; } function cloneFunction(func) { const bodyReg = /(?<={)(.|\n)+(?=})/m; const paramReg = /(?<=\().+(?=\)\s+{)/; const funcString = func.toString(); if (func.prototype) { const param = paramReg.exec(funcString); const body = bodyReg.exec(funcString); if (body) { if (param) { const paramArr = param[0].split(","); return new Function(...paramArr,body[0]); } else { return new Function(body[0]); } } else { return null; } } else { return eval(funcString); } } function cloneOtherType(targe,type) { const Ctor = targe.constructor; switch (type) { case boolTag: case numberTag: case stringTag: case errorTag: case dateTag: return new Ctor(targe); case rege程式設計客棧xpTag: return cloneReg(targe); case symbolTag: return cloneSymbol(targe); case funcTag: return cloneFunction(targe); default: return null; } } function clone(target,map = new WeakMap()) { // 克隆原始型別 if (!isObject(target)) { return target; } // 初始化 const type = getType(target); let cloneTarget; if (deepTag.includes(type)) { cloneTarget = getInit(target,type); } else { return cloneOtherType(target,type); } // 防止迴圈引用 if (map.get(target)) { return map.get(target); } map.set(target,cloneTarget); // 克隆set if (type === setTag) { target.forEach(value => { cloneTarget.add(clone(value,map)); }); return cloneTarget; } // 克隆map if (type === mapTag) { target.forEach((value,key) => { cloneTarget.set(key,clone(value,map)); }); return cloneTarget; } // 克隆物件和陣列 const keys = type === arrayTag ? undefined : Object.keys(target); forEach(keys || target,(value,key) => { if (keys) { key = value; } cloneTarget[key] = clone(target[key],map); }); return cloneTarget; } const map = new Map(); map.set("key","value"); map.set("ConardLi","code祕密花園"); const set = new Set(); set.add("ConardLi"); set.add("code祕密花園"); const target = { field1: 1,field2: undefined,field3: { child: "child" },field4: [2,4,8],empty: null,map,set,bool: new Boolean(true),num: new Number(2),str: new String(2),symbol: Object(Symbol(1)),date: new Date(),reg: /\d+/,error: new Error(),func1: () => { console.log("code祕密花園"); },func2: function(a,b) { return a + b; } }; const result = clone(target); console.log(target); console.log(result);
以上就是詳解JS變數儲存深拷貝和淺拷貝的詳細內容,更多關於JS變數儲存深拷貝和淺拷貝的資料請關注我們其它相關文章!