詳解js中的對象的深淺拷貝

前言

本文是整理的淺拷貝和深拷貝中涉及的知識點,在工作中是非常重要的,在面試中也是必考的,希望對小夥伴們有所幫助!
cmd-markdown-logo

爲什麼會產生深淺拷貝?

首先我們要知道一個流程

1,對象屬於引用類型的,以後瀏覽器會爲其開闢一個新的內存空間,併爲它分配一個16進制的地址

2,按照一定的順序,把對象的鍵值對存儲到內存空間

3,把開闢的內存地址賦值給變量(或事件),以後變量就通過地址找到內存空間,然後進行操作

基本數據類型和引用數據類型

數據分爲基本數據類型和引用數據類型

基本數據類型

String、Number、Boolean、Null、Undefined、Symbol

基本數據類型是直接存儲在棧中的數據

let str1 = '123';
str2 = str1;
str2 = '456';
console.log(str1); // '123'
console.log(str2); // '456'

形象舉例:

我之前買了一雙鞋子,我現在又買了一雙,某一雙壞了不會影響到另外一雙

引用數據類型

Array、Object

引用數據類型存儲的是該對象在棧中引用,真實的數據存儲在內存中

let arr1 =[1,2,3,4,5,6];
arr2 = arr1;
arr2.pop();
console.log(arr1);//[ 1, 2, 3, 4, 5 ]
console.log(arr2);//[ 1, 2, 3, 4, 5 ]

形象舉例:

在大草原上,有一些羊吃了腐爛的草得病死了。草原還是草原,但是內部的草變少了;羊羣還是羊羣,但是內部的羊變少了

深拷貝和淺拷貝的概念

淺拷貝:

僅僅複製對象的引用,而不是對象本身

深拷貝:

把複製的對象所引用的全部對象都複製一遍

深拷貝和淺拷貝的區別

cmd-markdown-logo
cmd-markdown-logo

~ 和原數據是否指向同一對象 第一層數據爲基本數據類型 原數據中包含子對象
賦值 改變會使數據一同改變 改變會使原數據一同改變
淺拷貝 改變不會使原數據一同改變 改變會使數據一同改變
深拷貝 改變不會使原數據一同改變 改變不會使原數據一同改變

淺拷貝

通用循環

const arr1 = [1, 2, ['ming', 'abc'], 5];

const shallowClone = (arr) => {
  const dst = [];
  for (let prop in arr) {
    if (arr.hasOwnProperty(prop)) {
        dst[prop] = arr[prop];
    }
  }
  return dst;
}

const arr2 = shallowClone(arr1);
arr2[2].push('wuhan');
arr2[3] = 5;

console.log(arr1);
console.log(arr2);

運行結果:
cmd-markdown-logo

object.assign()

  • Object.assign()方法可以把任意多個的源對象自身的可枚舉屬性拷貝給目標對象,然後返回目標對象
  • Object.assign()進行的是淺拷貝,拷貝的是對象的屬性的引用,而不是對象本身
    const obj1 = {
        username:'ming',
        skill:{
            play:['backetball','game'],
            rend: 'book',
        },
        girlfriends:['xiaomi','xiaohong','xiaolan'],
    };
    const obj2 = Object.assign({},obj1);
    obj2.username = 'memg';//修改基本類型
    obj2.skill.read = 'e-mail';//修改二層基本類型
    obj2.skill.play = ['footbool'];//修改二層引用類型
    obj2.girlfriend = ['xiaoming'];
    console.log(obj1);
    console.log(obj2);

運行結果:
cmd-markdown-logo

Array.prototype.concat()

concat()是數組的一個內置方法,用戶合併兩個或者多個數組

這個方法不會改變現有數組,而是返回一個新數組

  const arr1 = [1,{username: 'ming',},];
  let arr2 = arr1.concat();
  arr1[0] = 2;
  arr1[1].username = 'meng';
  console.log(arr1);
  console.log(arr2);

運行結果:
cmd-markdown-logo

Array.prototype.slice()

slice()也是數組的一個內置方法,該方法會返回一個新的對象

slice()不會改變原數組

const arr1 = [1,{username:'ming',},];
let arr2 = arr1.slice();
arr2[0] = 2;
arr2[1].username = 'meng'
console.log(arr1);
console.log(arr2); 

運行結果:
cmd-markdown-logo

obj展開運算符

展開運算符是ES6中新提出來的一種運算符

在拷貝數組、對象以及拼接數組等方面都可以使用

這步我們也可以嘗試下使用const obj2 = {…obj1}的形式進行淺拷貝

//拷貝數組
const arr1 = [1, 2, 3];
const arr2 = [...arr1]; // like arr.slice()
arr2.push(8); 

console.log(arr1); // [ 1, 2, 3 ]
console.log(arr2); // [ 1, 2, 3, 8 ]

//拷貝對象
const obj1 = {
  name: 'ming',
  arr1: ['9', '7', '6'],
  obj: {
    name: 'meng',
    arr2: ['7', '8', '9'],
  },
};
const obj2 = {...obj1};// like arr.slice()
obj2.name = 'ming2';
obj2.arr1 = ['null'];
obj2.obj.name = 'meng2';
obj2.obj.arr2 = ['null'];

console.log(obj1);
console.log(obj2);

運行結果:
cmd-markdown-logo

深拷貝

手動遞歸

function deepClone (sourceObj, targetObj) {
    let cloneObj = targetObj || {}
    if(!sourceObj || typeof sourceObj !== "object" || sourceObj.length === undefined){
        return sourceObj
    }
    if(sourceObj instanceof Array){
        cloneObj = sourceObj.concat()
    } else {
        for(let i in sourceObj){
            if (typeof sourceObj[i] === 'object') {
                cloneObj[i] = deepClone(sourceObj[i], {})
            } else {
                cloneObj[i] = sourceObj[i]
            }
        }
    }
    return cloneObj
}
let sourceObj = {
  a: 1,
  b: {
    a: 1
  },
  c: {
    a: 1,
    b: {
      a: 1
    }
  },
  d: function() {
    console.log('hello world')
  },
  e: [1, 2, 3]
}
let targetObj = deepClone(sourceObj, {})
targetObj.c.b.a = 9
console.log(sourceObj)
console.log(targetObj)

運行結果:
cmd-markdown-logo

Json.parse(Json.Stringify())

JSON.stringify():把一個js對象序列化爲一個JSON字符串

JSON.parse():把JSON字符串反序列化爲一個js對象

const arr9 = [
    1,
    {
      username:'ming',
    },
  ];
  let arr10 = JSON.parse(JSON.stringify(arr9));
  arr10[0]=2;
  arr10[1].username='meng';
  console.log(arr9);
  console.log(arr10);

運行結果:
cmd-markdown-logo

let obj = {
    name: 'ming',
    age: 20,
    friend: {
      name: 'ming1',
      age: 19
    }
  };
  let copyObj = JSON.parse(JSON.stringify(obj));
  obj.name = 'meng';
  obj.friend.name = 'meng1';
  console.log(obj);
  console.log(copyObj);

運行結果:
cmd-markdown-logo

函數庫Lodash

Lodash作爲JavaScript函數庫/工具庫,它裏面有非常好用的封裝好的功能

var _= require('lodash');
const obj1 = [
  1,
  'Hello!',
  { name:'ming1' },
  [
    {
      name:'meng1',
    }
  ],
]
const obj2 = _.cloneDeep(obj1);
obj2[0] = 2;
obj2[1] = 'Hi!';
obj2[2].name = 'ming2'
obj2[3][0].name = 'meng2';
console.log(obj1);
console.log(obj2);

運行結果:
cmd-markdown-logo

最後

如果本文對你有幫助得話,給本文點個贊❤️❤️❤️

歡迎大家加入,一起學習前端,共同進步!
cmd-markdown-logo
cmd-markdown-logo

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章