詳解JS變量存儲(chǔ)深拷貝和淺拷貝
變量類型與存儲(chǔ)空間
棧內(nèi)存和堆內(nèi)存

基本數(shù)據(jù)類型
string、number、null、undefined、boolean、symbol(ES6新增) 變量值存放在棧內(nèi)存中,可直接訪問(wèn)和修改變量的值
基本數(shù)據(jù)類型不存在拷貝,好比如說(shuō)你無(wú)法修改數(shù)值1的值
引用類型
Object Function RegExp Math Date 值為對(duì)象,存放在堆內(nèi)存中
在棧內(nèi)存中變量保存的是一個(gè)指針,指向?qū)?yīng)在堆內(nèi)存中的地址。
當(dāng)訪問(wèn)引用類型的時(shí)候,要先從棧中取出該對(duì)象的地址指針,然后再?gòu)亩褍?nèi)存中取得所需的數(shù)據(jù)
圖解存儲(chǔ)空間
let a1 = 0; // 棧內(nèi)存
let a2 = "this is string" // 棧內(nèi)存
let a3 = null; // 棧內(nèi)存
let b = { x: 10 }; // 變量b存在于棧中,{ x: 10 }作為對(duì)象存在于堆中
let c = [1, 2, 3]; // 變量c存在于棧中,[1, 2, 3]作為對(duì)象存在于堆中

引用類型的賦值
let a = { x: 10, y: 20 }
let b = a;
b.x = 5;
console.log(a.x); // 5

深拷貝和淺拷貝
深拷貝
將一個(gè)對(duì)象從內(nèi)存中完整的拷貝一份出來(lái),從堆內(nèi)存中開(kāi)辟一個(gè)新的區(qū)域存放新對(duì)象,且修改新對(duì)象不會(huì)影響原對(duì)象
淺拷貝
淺拷貝是按位拷貝對(duì)象,它會(huì)創(chuàng)建一個(gè)新對(duì)象,這個(gè)對(duì)象有著原始對(duì)象屬性值的一份精確拷貝。如果屬性是基本類型,拷貝的就是基本類型的值;如果屬性是內(nèi)存地址(引用類型),拷貝的就是內(nèi)存地址
對(duì)象的賦值
當(dāng)我們把一個(gè)對(duì)象賦值給一個(gè)新的變量時(shí),賦的其實(shí)是該對(duì)象的在棧中的地址,而不是堆中的數(shù)據(jù)。也就是兩個(gè)對(duì)象指向的是同一個(gè)存儲(chǔ)空間,無(wú)論哪個(gè)對(duì)象發(fā)生改變,其實(shí)都是改變的存儲(chǔ)空間的內(nèi)容,因此,兩個(gè)對(duì)象是聯(lián)動(dòng)的。
三者對(duì)比

淺拷貝的常用的五種方法
Object.assign()
Object.assign() 方法可以把任意多個(gè)的源對(duì)象自身的可枚舉屬性拷貝給目標(biāo)對(duì)象,然后返回目標(biāo)對(duì)象。但是 Object.assign()進(jìn)行的是淺拷貝
Object.assign 會(huì)從左往右遍歷源對(duì)象(sources)的所有屬性,然后用 = 賦值到目標(biāo)對(duì)象(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
擴(kuò)展運(yùn)算符
let obj = {a:1,b:{c:1}}
let obj2 = {...obj};
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() 方法返回一個(gè)新的數(shù)組對(duì)象,這一對(duì)象是一個(gè)由 begin和 end(不包括end)決定的原數(shù)組的淺拷貝。原始數(shù)組的基本類型不會(huì)被改變,引用類型會(huì)被改變。
let arr = [1, 3, {
username: ' kobe'
}];
let arr3 = arr.slice();
arr3[0]=0;
arr3[2].username = 'wade'
console.log(arr);
Array.prototype.concat()
let arr = [1, 3, {
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())
通過(guò)JSON.stringify實(shí)現(xiàn)深拷貝有幾點(diǎn)要注意
拷貝的對(duì)象的值中如果有函數(shù),undefined,symbol則經(jīng)過(guò)JSON.stringify()序列化后的JSON字符串中這個(gè)鍵值對(duì)會(huì)消失
無(wú)法拷貝不可枚舉的屬性,無(wú)法拷貝對(duì)象的原型鏈
拷貝Date引用類型會(huì)變成字符串
拷貝RegExp引用類型會(huì)變成空對(duì)象
對(duì)象中含有NaN、Infinity和-Infinity,則序列化的結(jié)果會(huì)變成null
無(wú)法拷貝對(duì)象的循環(huán)應(yīng)用(即obj[key] = obj)
let arr = [1, 3, {
username: ' kobe'
}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'duncan';
console.log(arr, arr4)
手寫乞丐版深拷貝
首先這個(gè)deepClone函數(shù)并不能復(fù)制不可枚舉的屬性以及Symbol類型
這里只是針對(duì)Object引用類型的值做的循環(huán)迭代,而對(duì)于Array,Date,RegExp,Error,Function引用類型無(wú)法正確拷貝
對(duì)象成環(huán),即循環(huán)引用 (例如: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;
}
};
皇帝版深拷貝
該實(shí)例來(lái)自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 regexpTag:
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);
}
// 防止循環(huán)引用
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;
}
// 克隆對(duì)象和數(shù)組
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變量存儲(chǔ)深拷貝和淺拷貝的詳細(xì)內(nèi)容,更多關(guān)于JS變量存儲(chǔ)深拷貝和淺拷貝的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
利用JS解決ie6不支持max-width,max-height問(wèn)題的方法
本篇文章主要介紹了利用JS解決ie6不支持max-width,max-height問(wèn)題的方法。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2014-01-01
javascript中使用css需要注意的地方小結(jié)
javascript中使用css需要注意的地方小結(jié),注意保留字問(wèn)題。2010-09-09
JS獲取html元素的標(biāo)記名實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇JS獲取html元素的標(biāo)記名實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10
JavaScript中條件語(yǔ)句的優(yōu)化技巧總結(jié)
這篇文章主要給大家介紹了關(guān)于JavaScript中條件語(yǔ)句的優(yōu)化技巧,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
javascript監(jiān)聽(tīng)鼠標(biāo)滾輪事件淺析
這篇文章主要介紹了javascript監(jiān)聽(tīng)鼠標(biāo)滾輪事件淺析,使用具體例子說(shuō)明,同時(shí)考慮了不同的瀏覽器,需要的朋友可以參考下2014-06-06
一文了解JavaScript中call/apply/bind的使用
這篇文章主要介紹了一文了解JavaScript中call/apply/bind的使用,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-07-07
javascript實(shí)現(xiàn)用戶管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)用戶管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
php實(shí)例分享之實(shí)現(xiàn)顯示網(wǎng)站運(yùn)行時(shí)間
這篇文章主要介紹了php實(shí)現(xiàn)顯示網(wǎng)站運(yùn)行時(shí)間,需要的朋友可以參考下2014-05-05

