JS 中的深拷貝與淺拷貝

前言 最近在寫項目的時候涉及到一些父子組件傳遞個對象或者數組通訊啥的,或者是直接複製添加對象啥的,直接使用賦值的時候總會出錯。一查原來是淺拷貝的問題,就從網上找了點資料,彙總到這裏來了。javascript

1 什麼是深拷貝&淺拷貝

見名知義,不管是深拷貝仍是淺拷貝,都是 copy 的問題。就是 copy 的時候出現的兩種狀況。區分起來也挺簡單的,舉個例子,假設 B 是 A 複製過來的,當咱們修改 A 的時候,B 也隨之改變了,那麼這個就是淺拷貝,那要是 B 沒有隨 A 一塊兒改變的話,那麼這個就深拷貝了。java

2 現實場景

首先呢,咱們先要明白在 Javascript 中,有 5 種簡單數據類型(也稱爲基本數據類型),分別是 Undefined,Null,String,Number,Boolean,還有 1 種複雜數據類型即 Object,(ES6 新出的 Symbol 數據類型就先不討論了)數組

2.1 基本數據類型

對於基本數據類型的複製就談不上什麼深拷貝和淺拷貝了,對於基本數據類型來講,他們的值在棧內存中佔據着固定大小的空間,並被保存在棧內存中。假設 變量 b 複製 基本數據類型變量 a,那麼 b 會內存中佔據本身的空間,和 a 就沒啥關係了,你們各管各的,互不干涉。函數

let a = 2; 
let b = a;
b = 4;
console.log(a); // 2
console.log(b); // 4

2.2 複雜數據類型(Object)

對於對象的話,他是引用類型,複製起來就要區分淺拷貝和深拷貝了,由於 Object 是引用類型,他真正的值保存在堆內存中,他在棧內存存儲是變量名和指向該對象值的指針(就是一個地址),以下圖所示。
image
因此當咱們用日常用一個變量去複製一個 Object 類型的變量的時候,複製的是他的指針地址而已,因此兩個變量最終都指向同一個變量,你們要改一塊兒改,這就是淺拷貝啦,以下spa

let obj1 = {name:'kk',age:12,desc:'源對象'}
let obj2 = obj1;
obj2.desc = '目標對象'
console.log(obj1); //{name:'kk',age:12,desc:'目標對象'} 此處源對象跟着一塊兒變了
console.log(obj2); // {name:'kk',age:12,desc:'目標對象'}

啥,不信?!,那就看圖
image
如何
image
可是在咱們平常的使用當中,Object 類型的淺拷貝的行爲會讓咱們很迷,我複製這個對象就是想複製他的值而已啦,不要複製人家個值就和他綁到一塊了,跟他一塊兒「同生共死」。因此啊,當咱們想按照咱們複製的想法,就只複製他的值用來本身用,他的是他的,個人是個人,你們井水不犯河水。接下來就要說咋辦了。3d

3 實現對象類型的深拷貝

對於對象的深拷貝,蒐集了網上的資料,就有下面三種方法指針

3.1 slice()&concat()

這個是針對數組的深拷貝,能夠經過這兩個方法實現對數組的深拷貝,以下code

let arr = [1,2,3,4]
let arr2 = arr.slice();
arr[0] = 0;
console.log(arr); //[1,2,3,4]
console.log(arr2); //[1,2,3,4]

concat 同理可得,不過這兩個方法有個問題,slice()concat() 方法可以深拷貝的就只有數組的一級屬性,可是若是是多維數組的話,那麼只有一級屬性的值是深拷貝,往下就都是淺拷貝了,以下所示cdn

let arr = [[1,2],2,3,4]
let arr2 = arr.concat();
arr[0][0] = 0;
arr[1] = 1;

console.log(arr); //[[0,2],1,3,4]

console.log(arr2); //[[0,2],2,3,4]
// arr2[1] 沒變,可是 arr2[0] 跟着一塊兒改了

image

3.2 JSON 的騷操做

經過 JSON 的 stringify, parase 操做也能夠實現對象的深拷貝。對象

let obj1 = {name:'kk',age:12,desc:'源對象'}
let obj2 = JSON.parase(JSON.stringify(obj1));
obj2.desc = '目標對象';
console.log(obj1); //{name:'kk',age:12,desc:'源對象'} 此處源對象就沒有一塊兒變了
console.log(obj2); // {name:'kk',age:12,desc:'目標對象'}

此法數組和對象均可以用。

3.3 本身寫一個深拷貝函數

本身動手,豐衣足食

function deepCopy(obj) {
  let newObj = Array.isArray(obj) ? [] : {};
  if (obj && typeof obj === "object") {
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        //判斷ojb子元素是否爲對象,若是是,遞歸複製
        if (obj[key] && typeof obj[key] === "object") {
          newObj[key] = deepCopy(obj[key]);
        } else {
          //若是不是,簡單複製
          newObj[key] = obj[key];
        }
      }
    }
  }
  return newObj;
}

let a = [1,2,3];
let b = deepCopy(a);
a[0] = 0;
console.log(a); //[0,2,3]
console.log(a); // [1,2,3]

3.4 JQ 的 extend 方法

這個就直接放文檔了
$.extend( [deep ], target, object1 [, objectN ] )
deep:若是設爲true,則遞歸合併即深拷貝。
target:待修改對象。
object1:待合併到第一個對象的對象。
objectN:待合併到第一個對象的對象。
使用以下

let a = [1,2,3],
let b = $.extend(true,[],a);
a[0]=1;
console.log(a); // [0,2,3]
console.log(b); // [1,2,3]

以上就是關於 JS 中的深拷貝與淺拷貝的知識和如何進行深拷貝的知識了,若是有錯或者有其餘方式的話,歡迎在下面留言評論啦

相關文章
相關標籤/搜索