對于這個問題可以從深拷貝和淺拷貝的使用或起源說起。
1. js變量包含兩種不同資料類型的值基本類型和引用類型
基本類型包括ES6新增的一共是6種,具體如下
string ,number, null ,undefined, boolean ,symbol
引用類型為那些可能由多個值構成的對象,隻有一種
object
将一個值賦給變量時,解析器必須先确定這個值是引用類型還是基本類型。
基本資料類型可以操作儲存在變量中的實際的值
引用類型的值是儲存在記憶體中的值,但是JavaScript不允許直接通路記憶體中的位置,就是說不能直接操作對象的記憶體空間。在操作對象時,我們實際上操作的是對象的引用而不是實際的值。
2. 變量存儲方式–棧和堆
棧:自動配置設定記憶體空間,自動釋放,裡面存放的是基本類型的值和引用類型的位址
堆:動态配置設定的記憶體,大小不定,不會自動釋放。裡面存放引用類型的值。
3. 值傳遞和址傳遞
基本類型和引用類型最大的差別就是傳值和傳址的差別
基本類型采用值傳遞
var a = 100;
var b = a;
b++;
console.log(a); //100
console.log(b); //101
引用類型則采用址傳遞,将存放在棧中的位址指派給變量
var a = [1,2,3];
var b = a;
b.push(4);
console.log(a); //[1,2,3,4]
console.log(b); //[1,2,3,4]
這裡我們本意隻想給數組b添加一個元素,然而數組a也增加了。
分析: 由于a和b都是引用類型,采用址傳遞,指派的時候是把a的位址指派給b,即a和b指向同一個位址,而這個位址都指向堆記憶體中引用類型的值,當b改變這個值的同時,因為a的位址也指向這個值,是以a的值也跟着變化。
要解決上面出現的問題,就是要使用深拷貝或者淺拷貝了。
首先,淺拷貝和深拷貝都隻針對于array、object這種複雜對象。
淺拷貝解決就是設定一個新的對象obj2,通過周遊的方式将obj1對象的值一一指派給obj2對象。
以下為淺拷貝的實作,比較簡單,其實就是周遊對象屬性的問題
//數組的淺拷貝
var a = [1, 2, 3];
var b = [];
for(var i in a) {
b[i] = a[i];
}
b.push(4);
console.log(a); // [1,2,3]
console.log(b); // [1,2,3,4]
//對象的淺拷貝
var obj1 = {
''a:1,
'b':2
};
var obj2 = {};
for (var i in obj1) {
obj2[i] = obj[i];
}
obj2['c'] = 3;
console.log(obj1); // {a: 1, b: 2 }
console.log(obj2); // {a:1, b: 2, c: 3 }
但上面的代碼隻能進行一層拷貝,無法進行深層次的拷貝,封裝函數後對數組的嵌套進行測試
function shallowClone (source) {
var target = {};
for (var i in source) {
if(source.hasOwnProperty (i) ) {
target[i] = source[i];
}
}
return target;
}
var obj1 = {
fruits: ['apple','banner'],
num: 100
}
var obj2 = shallowClone(obj1)
obj2.fruits[0] = 'orange'
console.log(obj1.fruits[0]); //orange
console.log(obj2.fruits[0]); //orange
結果證明無法進行深層次的拷貝,這個時候我們可以使用深拷貝來完成
以下為實作深拷貝的方法
深拷貝的問題其實可以分解成兩個問題,淺拷貝+遞歸,什麼意思呢?
看下面三個實作方法
- 用for…in實作周遊和複制
function deepClone(obj){
let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
//判斷ojb子元素是否為對象,如果是,遞歸複制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{
//如果不是,簡單複制
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a=[1,2,3,4],
b=deepClone(a);
a[0]=2;
console.log(a,b);
- JSON方法實作
let deepClone = function (obj) {
let temp = JSON.stringify(obj);
let result = JSON.parse(temp);
return result;
}
let obj1 = {
a: {
age: 20,
class: 1602
},
b: {
age: 21,
class: 1601
}
};
let obj2 = deepClone(obj1);
console.log(obj2);
- 利用數組的Array.prototype.forEach進copy
let deepClone = function (obj) {
let copy = Object.create(Object.getPrototypeOf(obj));
let propNames = Object.getOwnPropertyNames(obj);
propNames.forEach(function (items) {
let item = Object.getOwnPropertyDescriptor(obj, items);
Object.defineProperty(copy, items, item);
});
return copy;
};
let testObj = {
name: "weiqiujuan",
sex: "girl",
age: 22,
favorite: "play",
family: {brother: "wei", mother: "haha", father: "heihei"}
}
let testRes2 = deepClone(testObj);
console.log(testRes2);