JavaScript淺層克隆與深度克隆示例詳解
1 相關(guān)知識點
- 淺克隆就是將棧內(nèi)存中的引用復(fù)制一份,賦給一個新的變量,本質(zhì)上兩個指向堆內(nèi)存中的同一地址,內(nèi)容也相同,其中一個變化另一個內(nèi)容也會變化。
- 深克隆就是創(chuàng)建一個新的空對象,開辟一塊內(nèi)存,然后將原對象中的數(shù)據(jù)全部復(fù)制過去,完全切斷兩個對象間的聯(lián)系。
- 區(qū)別:淺克隆和深克隆最大的區(qū)別就是對引用值的處理了,即淺克隆之后你改我也改,深克隆之后你改我不改。(PS:原始值的處理一樣)
- 原始值(棧數(shù)據(jù)stack):Number,Boolean(false/true),String,undefined,null
- 引用值(堆數(shù)據(jù)heap):Array,Object,function ··· Date,RegExp
2 淺層克隆
在淺層克隆中,原始值的克隆沒問題,只是值的拷貝,不會出現(xiàn)你改我改的問題。但是引用值的克隆,就會出現(xiàn)你改我也改的問題,因為淺層克隆的是地址,即指向的是同一空間。
2.1 淺克隆函數(shù)
function clone(origin, target) {
var target = target || {};
//容錯,即防止用戶不傳遞目標參數(shù)。若用戶傳遞了參數(shù)就用,若沒傳則拿一個空對象當目標
for (var prop in origin) {
target[prop] = origin[prop];
}
return target;
}
2.2 運用實例
function clone(origin, target) {
var target = target || {};
for (var prop in origin) {
target[prop] = origin[prop];
}
return target;
}
var obj = {
name: 'abc',
age: '18',
sex: 'male',
card: ['a', 'b', 'c'],
book: {
name: 'ccc',
sbook: {
name: 'aaa'
}
}
};
var newobj = {};
clone(obj, newobj);
運行代碼如下:

3 深度克隆
進行深度克隆之后,對于引用值的克隆的問題就會和原始值一樣我改你不改,因為在深度克隆中雖然是相同的東西,但是指向不同的空間。即深度克隆之后,值各自獨立,互不影響。
3.1 深克隆步驟分析
需要進行深度克隆的對象如下:
var obj = {
name: 'abc', // 原始值
age: '18', // 原始值
sex: 'male',// 原始值
card: ['a', 'b', 'c'], // 引用值
book: { // 引用值
name: 'ccc', // 原始值
sbook: { // 引用值
name: 'aaa'// 原始值
}
}
};
var obj1 = {};
(1)首先需要遍歷要克隆的對象
方法:for (var prop in origin){···}
for (var prop in origin) {
···
}
(2)依次判斷是不是原始值
方法:typeof() ,即若為原始值,就直接拷貝;若為引用值(typeof(···)返回的值是object),則進行遞歸操作。需要注意是的typeof(null) == 'object',所以得排除這一個情況。
if (origin[prop] !== "null" && typeof(origin[prop]) == 'object') {
···
// 遞歸
} else {
target[prop] = origin[prop];
}
(3)判斷是數(shù)組還是對象
方法:toString(推薦), constructor,instanceof (后兩個會涉及到父子域的小問題,雖然遇到的可能不是很大)
var toStr = Object.prototype.toString,
arrStr = "[object Array]";
if (toStr.call(origin[prop]) == arrStr) {
··· // 數(shù)組
} else {
··· // 對象
}
(4)建立相應(yīng)的數(shù)組或?qū)ο?/strong>
方法:建立一個新的同名空數(shù)組 / 對象,并將原始對象中的 數(shù)組或?qū)ο?當成一個新的原始對象,再次將其中的數(shù)據(jù)拷貝到目標對象的 同名空數(shù)組 / 對象 里面。即遞歸開始拷貝數(shù)組或?qū)ο罄锩娴臄?shù)據(jù),并遞歸執(zhí)行第(1)步。遞歸完成之后,再依次進行下一個數(shù)據(jù)的克隆。
var toStr = Object.prototype.toString,
arrStr = "[object Array]";
if (toStr.call(origin[prop]) == arrStr) {
target[prop] = [];
} else {
target[prop] = {};
}
newobj = {
name: 'abc',
age: '18',
sex: 'male',
card: []
// 建立一個新的同名空數(shù)組,并把obj的card數(shù)據(jù)當成一個原始對象,再次拷貝到obj1的card里面
// 即 遞歸開始拷貝數(shù)組或?qū)ο罄锩娴臄?shù)據(jù),遞歸執(zhí)行第(1)步
// 執(zhí)行完數(shù)組card拷貝之后,開始同理拷貝下一個對象book···
}
3.2 深克隆函數(shù)
function deepClone(origin, target) {
var target = target || {},
toStr = Object.prototype.toString,
arrStr = "[object Array]";
for (var prop in origin) {
if (origin.hasOwnProperty(prop)) {
if (origin[prop] !== "null" && typeof(origin[prop]) == 'object') {
if (toStr.call(origin[prop]) == arrStr) {
target[prop] = [];
} else {
target[prop] = {};
}
deepClone(origin[prop], target[prop]);
} else {
target[prop] = origin[prop];
}
}
}
return target;
}
使用三目運算符簡化后的代碼如下:
// 使用三目運算符簡化后
function deepClone(origin, target) {
var target = (target || {}),
toStr = Object.prototype.toString,
arrStr = "[object Array]";
for (var prop in origin) {
if (origin.hasOwnProperty(prop)) {
if (origin[prop] !== "null" && typeof (origin[prop]) == 'object') {
target[prop] = toStr.call(origin[prop]) == arrStr ? [] : {};
deepClone(origin[prop], target[prop]);
} else {
target[prop] = origin[prop];
}
}
}
return target;
}
3.3 運用實例
// 使用三目運算符簡化后
function deepClone(origin, target) {
var target = (target || {}),
toStr = Object.prototype.toString,
arrStr = "[object Array]";
for (var prop in origin) {
if (origin.hasOwnProperty(prop)) {
if (origin[prop] !== "null" && typeof (origin[prop]) == 'object') {
target[prop] = toStr.call(origin[prop]) == arrStr ? [] : {};
deepClone(origin[prop], target[prop]);
} else {
target[prop] = origin[prop];
}
}
}
return target;
}
運行代碼如下:

3.4 hasOwnProperty
hasOwnProperty() 方法會返回一個布爾值,指示對象自身屬性中是否具有指定的屬性(即是否有指定的鍵)。
語法:obj.hasOwnProperty(prop)
參數(shù):要檢測的屬性的字符串形式表示的名稱,或者Symbol。
返回值:用來判斷某個對象是否含有指定的屬性的布爾值。
描述:所有繼承了Object的對象都會繼承到hasOwnProperty方法。這個方法可以用來檢測一個對象是否含有特定的自身屬性;和in運算符不同,該方法會忽略掉那些從原型鏈上繼承到的屬性。
用法:
a. 使用hasOwnProperty方法判斷屬性是否存在
b. 區(qū)別自身屬性與繼承屬性
c. 遍歷一個對象的所有自身屬性
d. 使用hasOwnProperty作為屬性名
具體知識點請參考 Object.prototype.hasOwnProperty()
若對象里面編寫了原型屬性,但遍歷的時候并不想讓其顯示出來,就可以使用對象名.hasOwnProperty(屬性名) 來判斷是否是自身屬性,若是自己的則返回值為true,若不是自身原型屬性則返回值為false。實例如下:
var obj = {
name: 'ABC',
age: '18',
sex: 'male',
__proto__: {
heart: 'happy'
}
}
for (var prop in obj) {
// 配套使用,起到一個過濾的作用,不把原型鏈上的數(shù)據(jù)弄出來
if (obj.hasOwnProperty(prop)) {
console.log(obj[prop]);// ABC 18 male
}
}
個人筆記,歡迎大家交流探討!
總結(jié)
到此這篇關(guān)于JavaScript淺層克隆與深度克隆的文章就介紹到這了,更多相關(guān)JavaScript淺層克隆與深度克隆內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript與css3動畫結(jié)合使用小結(jié)
本文給大家講述的是如何使用javascript結(jié)合CSS動畫來實現(xiàn)一些占用資源更少,更優(yōu)化的動畫效果,思路十分巧妙,這里推薦給小伙伴們參考下。2015-03-03

