目錄
一.數(shù)據(jù)類型
二.淺拷貝與深拷貝
三.賦值和淺拷貝的區(qū)別
四.淺拷貝的實現(xiàn)方式
五.深拷貝的實現(xiàn)方式
一.數(shù)據(jù)類型
數(shù)據(jù)分為基本數(shù)據(jù)類型(String, Number, Boolean, Null, Undefined卸夕,Symbol)和對象數(shù)據(jù)類型。
基本數(shù)據(jù)類型:
訪問:基本數(shù)據(jù)類型的值是按值訪問的。
存儲:基本類型的變量是存放在棧內(nèi)存(Stack)里的。
圖解 : 棧內(nèi)存中包括了變量的標(biāo)示符和變量的值。
引用數(shù)據(jù)類型的特點:存儲的是該對象在棧中引用挤庇,真實的數(shù)據(jù)存放在堆內(nèi)存里
引用數(shù)據(jù)類型在棧中儲存了指針拐格,該指針指向堆中該實體的起始地址吵取。當(dāng)解釋器尋找引用值時逐虚,會首先檢索其在棧中的地址,取得地址后從堆中獲取實體谆膳。
訪問:引用類型的值是按引用訪問的叭爱。
存儲:引用類型的值是保存在堆內(nèi)存(Heap)中的對象(Object)。
圖解:
棧內(nèi)存中包含了變量的標(biāo)示符和指向堆內(nèi)存中該對象的指針
堆內(nèi)存中包含了對象的內(nèi)容
JavaScript 不能直接操作對象的內(nèi)存空間(堆內(nèi)存)
一.淺拷貝與深拷貝
深拷貝和淺拷貝是只針對Object和Array這樣的引用數(shù)據(jù)類型的漱病。
深拷貝和淺拷貝的示意圖大致如下:
淺拷貝只復(fù)制指向某個對象的指針买雾,而不是復(fù)制對象本身 新舊對象還是共享同一塊內(nèi)存。
但深拷貝會另外創(chuàng)造一個一摸一樣的對象杨帽, 新對象跟原對象不共享內(nèi)存漓穿,修改新對象不會改到原對象。
三.賦值和淺拷貝的區(qū)別
當(dāng)我們把一個對象賦值給一個新的變量時注盈,賦的其實是該對象的在棧中的地址晃危,而不是堆中的數(shù)據(jù)姊舵。也就是兩個對象指向的是同一個存儲空間,無論哪個對象發(fā)生改變孟抗,其實都是改變的存儲空間的內(nèi)容疫诽,因此,兩個對象是聯(lián)動的鳍鸵。
淺拷貝是按位拷貝對象苇瓣,它會創(chuàng)建一個新對象,這個對象有著原始對象屬性值的一份精確拷貝偿乖。如果屬性是基本類型击罪,拷貝的就是基本類型的值;如果屬性是內(nèi)存地址(引用類型)贪薪,拷貝的就是內(nèi)存地址 媳禁,因此如果其中一個對象改變了這個地址,就會影響到另一個對象古掏。即默認(rèn)拷貝構(gòu)造函數(shù)只是對對象進行淺拷貝復(fù)制(逐個成員依次拷貝)损话,即只復(fù)制對象空間而不復(fù)制資源。
// 對象賦值
var obj1 = {
'name' : 'zhangsan',
'age' : '18',
'language' : [1,[2,3],[4,5]],
};
var obj2 = obj1;
obj2.name = "lisi";
obj2.language[1] = ["二","三"];
console.log('obj1',obj1)
console.log('obj2',obj2)
// 淺拷貝
var obj1 = {
'name' : 'zhangsan',
'age' : '18',
'language' : [1,[2,3],[4,5]],
};
var obj3 = shallowCopy(obj1);
obj3.name = "lisi";
obj3.language[1] = ["二","三"];
function shallowCopy(src) {
var dst = {};
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
dst[prop] = src[prop];
}
}
return dst;
}
console.log('obj1',obj1)
console.log('obj3',obj3)
上面例子中槽唾,obj1是原始數(shù)據(jù)丧枪,obj2是賦值操作得到,而obj3淺拷貝得到庞萍。我們可以很清晰看到對原始數(shù)據(jù)的影響拧烦,具體請看下表:
四.淺拷貝的實現(xiàn)方式
1.Object.assign()
Object.assign() 方法可以把任意多個的源對象自身的可枚舉屬性拷貝給目標(biāo)對象,然后返回目標(biāo)對象钝计。但是 Object.assign()進行的是淺拷貝恋博,拷貝的是對象的屬性的引用,而不是對象本身私恬。
var obj = { a: {a: "xxxc", b: 19} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "wewd";
console.log(obj.a.a); //wewd
注意:當(dāng)object只有一層的時候债沮,是深拷貝
let obj = {
username: 'jsx'
};
let obj2 = Object.assign({},obj);
obj2.username = 'wawa';
console.log(obj);//{username: "jsx"}
2.Array.prototype.concat()
let arr = [1, 3, {
username: 'kobe'
}];
let arr2=arr.concat();
arr2[2].username = 'wade';
console.log(arr);
修改新對象會改到原對象:
3.
Array.prototype.slice()
let arr = [1, 3, {
username: ' kobe'
}];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr);
修改新對象會改到原對象:
Array的slice和concat方法不修改原數(shù)組,只會返回一個淺復(fù)制了原數(shù)組中的元素的一個新數(shù)組本鸣。
原數(shù)組的元素會按照下述規(guī)則拷貝:
- 如果該元素是個對象引用(不是實際的對象)疫衩,slice 會拷貝這個對象引用到新的數(shù)組里。兩個對象引用都引用了同一個對象荣德。如果被引用的對象發(fā)生改變闷煤,則新的和原來的數(shù)組中的這個元素也會發(fā)生改變。
- 對于字符串涮瞻、數(shù)字及布爾值來說(不是 String鲤拿、Number 或者 Boolean 對象),slice 會拷貝這些值到新的數(shù)組里署咽。在別的數(shù)組里修改這些字符串或數(shù)字或是布爾值近顷,將不會影響另一個數(shù)組。
let arr = [1, 3, {
username: ' kobe'
}];
let arr3 = arr.slice();
arr3[1] = 2
console.log(arr,arr3);
五.深拷貝的實現(xiàn)方式
1.遞歸方法實現(xiàn)深度克隆原理:遍歷對象、數(shù)組直到里邊都是基本數(shù)據(jù)類型幕庐,然后再去復(fù)制久锥,就是深度拷貝
var deepCopy = function(obj) {
if (typeof obj !== 'object') return obj //判斷不是object就直接返回
var newObj = (Object.prototype.toString.call(obj) === '[object Array]') ? [] : {} //判斷復(fù)制的目標(biāo)是數(shù)組還是對象
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = (typeof obj[key] !== 'object') ? obj[key]: deepCopy(obj[key]) // 如果值是對象,就遞歸一下异剥。 如果不是瑟由,就直接賦值
}
}
return newObj
}
就這么簡單。
類似的方法都是挺雷同的
function deepClone(source){
const targetObj = source.constructor === Array ? [] : {}; // 判斷復(fù)制的目標(biāo)是數(shù)組還是對象
for(let keys in source){ // 遍歷目標(biāo)
if(source.hasOwnProperty(keys)){
if(source[keys] && typeof source[keys] === 'object'){ // 如果值是對象冤寿,就遞歸一下
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
}else{ // 如果不是歹苦,就直接賦值
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
/*原理都一樣*/
var deepClone=function(source) {
if (!source && typeof source !== 'object') {
throw new Error('error arguments', 'deepClone')
}
const targetObj = source.constructor === Array ? [] : {}
Object.keys(source).forEach(keys => {
if (source[keys] && typeof source[keys] === 'object') {
targetObj[keys] = deepClone(source[keys])
} else {
targetObj[keys] = source[keys]
}
})
return targetObj
}
2.除此之外還有 函數(shù)庫 lodash
該函數(shù)庫也有提供_.cloneDeep用來做 Deep Copy
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false
JSON.parse(JSON.stringfify(obj))
3.JSON.parse(JSON.stringfify(obj))
原理:
用JSON.stringify將對象轉(zhuǎn)成JSON字符串,
再用JSON.parse()把字符串解析成對象督怜,一去一來殴瘦,新的對象產(chǎn)生了,而且對象會開辟新的棧号杠,實現(xiàn)深拷貝蚪腋。
可以實現(xiàn)數(shù)組或?qū)ο笊羁截?但不能處理函數(shù)
因為JSON.stringify() 方法是將一個JavaScript值(對象或者數(shù)組)轉(zhuǎn)換為一個 JSON字符串,不能接受函數(shù)
可以自行試驗 就不做圖例了姨蟋,太懶了