前言
淺拷貝和深拷貝在前端開發(fā)中是非常重要的知識(shí)點(diǎn),有時(shí)候面試官也非常喜歡問到這點(diǎn)劣摇,相信很多人只是聽過這兩個(gè)詞,不明白他們的意思和涵義弓乙,這里將會(huì)淺顯的講解淺拷貝和深拷貝末融。總結(jié)學(xué)習(xí)的同時(shí)暇韧,希望大家也會(huì)有點(diǎn)收獲勾习。
Javascript的兩種變量類型
1、JavaScript變量的類型分為兩種: 基本類型和引用類型懈玻。
- 基本類型是指簡(jiǎn)單的數(shù)據(jù)段巧婶,有5種類型:
Undefined
、Null
涂乌、Boolean
艺栈、Number
和String
- 引用類型是指可能有多個(gè)值構(gòu)成的對(duì)象,一般為:
Object
湾盒、Array
湿右、function
等
2、JavaScript內(nèi)存模型圖:
- 基本數(shù)據(jù)類型的特點(diǎn):直接存儲(chǔ)在棧(stack)中的數(shù)據(jù)
- 引用數(shù)據(jù)類型的特點(diǎn):存儲(chǔ)的是該對(duì)象在棧中引用即地址历涝,真實(shí)的數(shù)據(jù)存放在堆內(nèi)存里
3诅需、基本類型是按值訪問的,不會(huì)影響到其他數(shù)據(jù)荧库,而引用類型的值是按地址訪問的堰塌,簡(jiǎn)單的賦值,實(shí)際上只是把地址復(fù)制了一遍分衫,修改任意一個(gè)值會(huì)影響到另外一個(gè)场刑。
// 1、基本數(shù)據(jù)類型蚪战,不影響其他變量
var a = '沒修改'
var b = a
a = '被修改'
b // '沒修改'
// 2牵现、引用數(shù)據(jù)類型修改,會(huì)影響其他變量
var obj1 = {a: '沒修改'}
var obj2 = obj1
obj1.a = '被修改'
console.log(obj2) // {a: '被修改'}
從上面可以看出邀桑,基本數(shù)據(jù)類型瞎疼,不影響其他變量,所以基本類型的值沒有深拷貝的概念壁畸。而對(duì)象obj1
賦值給了數(shù)組obj2
,JavaScript
引擎只是將obj1
的地址賦值給了obj2
贼急,他們指向同一個(gè)內(nèi)存地址茅茂,并沒有開辟新的棧,當(dāng)修改obj1
的值太抓,obj2
也被影響了空闲,這就是淺拷貝當(dāng)然很多時(shí)候我們并不希望這樣。
深拷貝和淺拷貝
深拷貝和淺拷貝的示意圖大致如下:
淺拷貝只復(fù)制指向某個(gè)對(duì)象的指針走敌,而不復(fù)制對(duì)象本身碴倾,新舊對(duì)象還是共享同一塊內(nèi)存。但深拷貝會(huì)另外創(chuàng)造一個(gè)一模一樣的對(duì)象掉丽,新對(duì)象跟原對(duì)象不共享內(nèi)存跌榔,修改新對(duì)象不會(huì)改到原對(duì)象。
淺拷貝机打、深拷貝和賦值的區(qū)別
賦值操作則是兩個(gè)對(duì)象一起指向一個(gè)對(duì)象矫户,無論誰改變了對(duì)象里面的內(nèi)容,都會(huì)互相影響残邀。
淺拷貝和深拷貝都是將拷貝的內(nèi)容放入新的對(duì)象中去,區(qū)別在于淺拷貝只會(huì)新增一個(gè)外層對(duì)象來放要拷貝對(duì)象的所有內(nèi)容柑蛇,所以如果要拷貝的對(duì)象里面的屬性是對(duì)象則拷貝的是對(duì)象的引用芥挣,而深拷貝則是被拷貝對(duì)象包含多少對(duì)象,深拷貝就會(huì)對(duì)應(yīng)生成多少對(duì)象來一一對(duì)應(yīng)往里裝被拷貝對(duì)象里的內(nèi)容耻台,所有的對(duì)象都是獨(dú)立全新的拷貝一份空免。
簡(jiǎn)單來說:
- 賦值操作兩個(gè)變量指向同一個(gè)對(duì)象,兩者互相影響盆耽。
- 淺拷貝新生成一個(gè)對(duì)象蹋砚,對(duì)象里面的屬性是基本類型,拷貝的就是基本類型的值摄杂;如果屬性是內(nèi)存地址(引用類型)坝咐,拷貝的就是內(nèi)存地址。
- 深拷貝會(huì)新生成所有對(duì)象(值對(duì)象里面層層嵌套對(duì)象)析恢,對(duì)象里面的屬性是基本類型墨坚,拷貝的就是基本類型的值;如果屬性是內(nèi)存地址(引用類型)映挂,會(huì)新生成一個(gè)對(duì)象將內(nèi)容拷貝進(jìn)去泽篮。
淺拷貝的實(shí)現(xiàn)方式
1、Object.assign()實(shí)現(xiàn)
Object.assign()
方法可以把任意多個(gè)的源對(duì)象自身的可枚舉屬性拷貝給目標(biāo)對(duì)象柑船,然后返回目標(biāo)對(duì)象帽撑。但是Object.assign()
進(jìn)行的是淺拷貝,拷貝的是對(duì)象的屬性的引用鞍时,而不是對(duì)象本身亏拉。
var obj1 = { a: {a: "hello", b: 21} };
var obj2 = Object.assign({}, obj1);
obj2.a.a = "changed";
console.log(obj1.a.a); // "changed"
如果對(duì)象里面的屬性不是對(duì)象历恐,則進(jìn)行的是深拷貝。
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = Object.assign({}, obj1);
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒有受到影響
console.log(obj2);
// { a: 10, b: 100, c: 30 }
深拷貝的實(shí)現(xiàn)方式
1专筷、JSON.parse(JSON.stringify())實(shí)現(xiàn)
用JSON.stringify
把對(duì)象轉(zhuǎn)成字符串弱贼,再用JSON.parse
把字符串轉(zhuǎn)成新的對(duì)象。
var obj1 = { a: { b: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.a.b = 20;
console.log(obj1);
// { a: { b: 10 } } <-- 沒被修改
console.log(obj2);
// { a: { b: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.a === obj2.a);
// false
2磷蛹、遞歸拷貝
遞歸方法實(shí)現(xiàn)深度克隆原理:遍歷對(duì)象吮旅、數(shù)組直到里邊都是基本數(shù)據(jù)類型,然后再去復(fù)制味咳,就是深度拷貝庇勃。
var obj1 = {a: {name: '小紅'}, b: 2, arr:[1,2]}
var obj2 = {}
//參數(shù):初始值,完成值
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
//判斷是否引用類型槽驶,object,Array 的typeof檢測(cè) 都是object
if (typeof initalObj[i] === 'object') {
//遞歸前责嚷,判斷是對(duì)象還是數(shù)字,初始化
obj[i] = (initalObj[i].constructor === Array) ? [] : {};
//遞歸自己
arguments.callee(initalObj[i], obj[i]);
} else {
//基礎(chǔ)類型值 直接復(fù)制
obj[i] = initalObj[i];
}
}
return obj;
}
deepClone(obj1, obj2)
console.log(obj2) //{a: {name: '小紅'}, b: 2, arr:[1,2]}
3掂铐、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
4罕拂、Object.create()實(shí)現(xiàn)
直接使用var newObj = Object.create(oldObj),可以達(dá)到深拷貝的效果全陨。
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i];
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
} else {
obj[i] = prop;
}
}
return obj;
}
最后
深拷貝的話常見的是使用JSON.parse(JSON.stringify())
實(shí)現(xiàn),方法比較簡(jiǎn)單方便爆班。
更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,歡迎帥哥美女前來StarH枰獭J疗小!