深拷貝和淺拷貝的簡要詳解

司徒赫發表於2020-12-18

在前端面試題過程中,會遇到一些可能是你見過,但是實際上並沒有運用過的問題。幾天前,我就遇到了這個淺拷貝和深拷貝的問題,特意騰出來一點時間來整理一下,希望能幫到一些有點小迷惑的同學。。。
深拷貝和淺拷貝的概念
深拷貝和淺拷貝是針對複雜資料型別來說的,淺拷貝只拷貝一層,而深拷貝是層層拷貝。
深拷貝複製變數值,對於非基本型別的變數,則遞迴至基本型別變數後,再複製。深拷貝後的物件與原來的物件是完全隔離的,互不影響,對一個物件的修改並不會影響另一個物件。
淺拷貝是會將物件的每個屬性進行依次複製,但是當物件的屬性值是引用型別時,實質複製的是其引用,當引用指向的值改變時也會跟著變化。
可以使用 for in、 Object.assign、 擴充套件運算子 … 、Array.prototype.slice()、Array.prototype.concat() 等
var array = [
{ number: 1 },
{ number: 2 },
{ number: 3 }
];
var copyArray = array.slice();
copyArray[0].number = 100;
console.log(array);
console.log(copyArray);
let obj = {
name: ‘Yvette’,
age: 18,
hobbies: [‘reading’, ‘photography’]
}
let obj2 = Object.assign({}, obj);
let obj3 = {…obj};

 obj.name = 'Jack';
 obj.hobbies.push('coding');
 console.log(obj);
 console.log(obj2);
 console.log(obj3);

由此可以看出淺拷貝只最第一層屬性進行了拷貝,當第一層的屬性值是基本資料型別時,新的物件和原物件互不影響,但是如果第一層的屬性值是複雜資料型別,那麼新物件和原物件的屬性值其指向的是同一塊記憶體地址。
1.深拷貝最簡單的實現是: JSON.parse(JSON.stringify(obj))
JSON.parse(JSON.stringify(obj)) 是最簡單的實現方式,但是有一些缺陷:

   物件的屬性值是函式時,無法拷貝。
   原型鏈上的屬性無法拷貝
   不能正確的處理 Date 型別的資料
   不能處理 RegExp
   會忽略 symbol
   會忽略 undefined
       var deepCopy = function(a) {
         return JSON.parse(JSON.stringify(a));
      }
      a = {name:'aaa',people:{name: 'abc'}};b = deepCopy(a);b.people.name = 'def';  
       console.log(a,b)
       a = [1,2, {name: 'aaa'}];b = deepCopy(a);b[2].name = 'bbb';  
       console.log(a,b)

      a = {name:'aaa',fun:function(){console.log('fun');},nn: undefined};
       b = deepCopy(a); 
      console.log(a,b)  

       1.上述的方法會忽略值為function以及undefined的欄位,而且對date型別的支援也不太友好
       2.上述方法只能克隆原始物件自身的值,不能克隆它繼承的值 
       function Person (name) {
         this.name = name
      }
       var a = new Person('王二');
       var b = deepCopy(a);
       console.log(a.constructor == Person); // true
       console.log(b.constructor == Object); // true



2.實現一個 deepClone 函式  (深拷貝,完美)
   如果是基本資料型別,直接返回
   如果是 RegExp 或者 Date 型別,返回對應型別
   如果是複雜資料型別,遞迴。
   考慮迴圈引用的問題
var show={
    btn:"btn",
    init:function(){
        var that=this;
        alert(this);
        this.btn.click(function(){
                that.change();
                alert(this);
        })            
    },
    change:function(){
        this.btn.css({'background':'green'});
        person={
          name:"king",
          show:function(){
            console.log(this.name)
          }
        }
    }
}
  
  
function deepClone(obj, hash = new WeakMap()) { //遞迴拷貝
    if (obj instanceof RegExp) return new RegExp(obj);
    if (obj instanceof Date) return new Date(obj);
    if (obj === null || typeof obj !== 'object') {
        //如果不是複雜資料型別,直接返回
        return obj;
    }
    if (hash.has(obj)) {
        return hash.get(obj);
    }
    /**
     * 如果obj是陣列,那麼 obj.constructor 是 [Function: Array]
     * 如果obj是物件,那麼 obj.constructor 是 [Function: Object]
     */
    let t = new obj.constructor();
    hash.set(obj, t);
    for (let key in obj) {
        //遞迴
        if (obj.hasOwnProperty(key)) {//是否是自身的屬性
            t[key] = deepClone(obj[key], hash);
        }
    }
    return t;
}

var show2 = cloneObject(show)

console.log(show2)

	//遞迴函式   
	function cloneObject (obj) {
	var newObj = {}  //如果不是引用型別,直接返回
	if (typeof (obj) !== 'object') {
		  return obj
	}
	 //如果是引用型別,遍歷屬性
	else{
		for (var attr in obj) {
		//如果某個屬性還是引用型別,遞迴呼叫
		   newObj[attr] = cloneObject(obj[attr])
		}
	}
	return newObj
	}

相關文章