天天看點

js深淺拷貝

1. 是什麼

假設B複制了A,當修改A時,看B是否會發生變化:

如果B也跟着變了,說明這是淺拷貝。牆頭草,你變我也變。

如果B沒變,那就是深拷貝。

2. 産生的原因

JS存在基本類型和引用類型。

  1. 基本類型指的是簡單的資料段。我們複制他的時候,會建立新值,并把它放在一個新的變量的記憶體位址。

    基本類型:Number Boolean String undefined null

    複制:

    js深淺拷貝
  2. 引用類型指的是一個對象,并且我們對對象的操作都隻是在操作它的引用而已。

    引用類型:object,array,function,error,date

複制:

js深淺拷貝

深淺拷貝是針對引用類型的。

3. 實作淺拷貝

3.1 array和object通用–循環複制
var shallowCopy = function(obj) {
   // 判斷obj的類型是否是object類型
    if (typeof obj !== 'object') return;
    var newObj = obj instanceof Array ? [] : {};
    // 周遊obj,并且判斷是obj的屬性才拷貝
    for (var key in obj) {
    // 不周遊原型鍊上的屬性,隻周遊自身屬性
        if (obj.hasOwnProperty(key)) {
            newObj[key] = obj[key];
        }
    }
    return newObj;
}      
var obj={
  name:'123',
  age:10,
}
var newObj=shallowCopy(obj);
obj.name='213';
console.log(newObj.name);
輸出:"123”  随之改變。      
3.2 數組淺拷貝–slice()和contact()!!!

數組的資料類型是引用類型,在這種情況下是淺拷貝。

數組的資料類型是基本類型,在這種情況下是深拷貝。

淺拷貝:

var arr = [{old: 'old'}, ['old']];
var newArr1 = arr.concat();
var newArr2=arr.slice();

arr[0].old = 'new';

console.log(arr) // [{old: 'new'}, ['new']]
console.log(newArr1) // [{old: 'new'}, ['new']]
console.log(newArr2) // [{old: 'new'}, ['new']]      

深拷貝:

var arr = ['old', 1, true, null, undefined];
var newArr1 = arr.concat();
var newArr2=arr.slice();

arr[0] = 'new';

console.log(arr) // [{old: 'new'}, ['new']]
console.log(newArr1) // [{old: 'old}, ['new']]
console.log(newArr2) // [{old: 'old'}, ['new']]      
3.3 對象的淺拷貝–Object.assign !!!

這裡的Object.assign也是一個很奇怪的點,它既是深拷貝又是淺拷貝。一級屬性-深拷貝,深層屬性-淺拷貝

var obj = { a: 0 , b: { c: 0}}; 
var newObj = Object.assign({}, obj); 

obj.a = 1; 
obj.b.c = 3; 
console.log(obj);        // { a: 1 , b: { c: 3}}; 
console.log(newObj);    // { a: 0 , b: { c: 3}}; 
可以很清楚的發現一級深拷貝,二級淺拷貝。      
3.4 對象的淺拷貝-- 展開運算符(…)!!!

這裡的展開運算符(…)同樣是一個很奇怪的點,它既是深拷貝又是淺拷貝。一級屬性-深拷貝,深層屬性-淺拷貝

var obj = { a: 0 , b: { c: 0}}; 
var newObj = {...obj}; 

obj.a = 1; 
obj.b.c = 3; 
console.log(obj);        // { a: 1 , b: { c: 3}}; 
console.log(newObj);    // { a: 0 , b: { c: 3}}; 
可以很清楚的發現一級深拷貝,二級淺拷貝。      

4. 深拷貝

在淺拷貝中已經介紹而來幾種深拷貝的方式。

4.1 JSON.parse(JSON.stringify())

數組和對象均可用

數組

var arr = [1, 3, {
    name: ' syl'
}];
var newArr = JSON.parse(JSON.stringify(arr));
arr[2].name = '123'; 
console.log(newArr[2]); /// 'syl ' 沒有改變
      

對象

var obj={
    name:'syl',
}
var newObj=JSON.parse(JSON.stringify(obj));
obj.name="123";
console.log(newObj.name); //'syl'. 沒有改變      

這種方法雖然可以實作數組或對象深拷貝,但不能處理函數

var  arr = [1, 3, {
    name: ' syl'
},function(){}];
var newArr = JSON.parse(JSON.stringify(arr));

console.log(newArr[3]) // null ,沒有輸出函數。      

這是因為JSON.stringify() 方法是将一個JavaScript值(對象或者數組)轉換為一個 JSON字元串,不能接受函數

4.2 深拷貝通用–遞歸
function deepCopy(obj) {
  //是否是數組或對象
  if (typeof obj !== 'object') return; 
  
  var newObj = obj instanceof Array ? [] : {};
  
  for (var key in obj) {
  
        // 不周遊原型鍊上的屬性,隻周遊自身屬性
    if (obj.hasOwnProperty(key)) {
    
         // 如果值是對象,就遞歸一下
      if (obj[keys] && typeof obj[key ] === "object") {
       
        newObj[key] = obj[key] instanceof Array ?[] : {};
        // 如果是引用資料類型,會遞歸調用
        newObj[key] =  deepCopy(obj[key]);
      } else {
      
        // 如果不是,就直接指派
        newObj = obj[keys];
        
      }
    }
  }
  return result;
}

      

補充:

淺拷貝:es6中有兩種新方法

方法1:

` let [...spread]= [12, 5, 8, 130, 44];      

方法2:

Array.from(array)//建立一個新數組      

繼續閱讀