亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

深拷貝怎么實現

發布時間:2021-01-18 13:40:31 來源:億速云 閱讀:203 作者:小新 欄目:互聯網科技

這篇文章主要介紹深拷貝怎么實現,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

深拷貝的三種實現方式分別是:1、遞歸遞歸去復制所有層級屬性;2、用JSON對象的parse和stringify實現;3、借用JQ的extend方法。

深拷貝的三種實現方式分別是:

1、遞歸遞歸去復制所有層級屬性

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);

深拷貝怎么實現

跟之前想象的一樣,現在b脫離了a的控制,不再受a影響了。

這里再次強調,深拷貝,是拷貝對象各個層級的屬性,可以看個例子。JQ里有一個extend方法也可以拷貝對象,我們來看看

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

深拷貝怎么實現

那是不是說slice方法也是深拷貝了,畢竟b也沒受a的影響,上面說了,深拷貝是會拷貝所有層級的屬性,還是這個例子,我們把a改改

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

深拷貝怎么實現

拷貝的不徹底啊,b對象的一級屬性確實不受影響了,但是二級屬性還是沒能拷貝成功,仍然脫離不了a的控制,說明slice根本不是真正的深拷貝。

這里引用知乎問答里面的一張圖

深拷貝怎么實現

第一層的屬性確實深拷貝,擁有了獨立的內存,但更深的屬性卻仍然公用了地址,所以才會造成上面的問題。

同理,concat方法與slice也存在這樣的情況,他們都不是真正的深拷貝,這里需要注意。

2.除了遞歸,我們還可以借用JSON對象的parse和stringify

function deepClone(obj){
    let _obj = JSON.stringify(obj),
        objClone = JSON.parse(_obj);
    return objClone
}    
let a=[0,1,[2,3],4],
    b=deepClone(a);
a[0]=1;
a[2][0]=1;
console.log(a,b);

深拷貝怎么實現

可以看到,這下b是完全不受a的影響了。

附帶說下,JSON.stringify與JSON.parse除了實現深拷貝,還能結合localStorage實現對象數組存儲。有興趣可以閱讀博主這篇文章。

localStorage存儲數組,對象,localStorage,sessionStorage存儲數組對象

3.除了上面兩種方法之外,我們還可以借用JQ的extend方法。

$.extend( [deep ], target, object1 [, objectN ] )

  • deep表示是否深拷貝,為true為深拷貝,為false,則為淺拷貝

  • target Object類型 目標對象,其他對象的成員屬性將被附加到該對象上。

  • object1  objectN可選。 Object類型 第一個以及第N個被合并的對象。

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

可以看到,效果與上面方法一樣,只是需要依賴JQ庫。

深拷貝怎么實現

說了這么多,了解深拷貝也不僅僅是為了應付面試題,在實際開發中也是非常有用的。例如后臺返回了一堆數據,你需要對這堆數據做操作,但多人開發情況下,你是沒辦法明確這堆數據是否有其它功能也需要使用,直接修改可能會造成隱性問題,深拷貝能幫你更安全安心的去操作數據,根據實際情況來使用深拷貝,大概就是這個意思。

4.lodash的_.cloneDeep()

以下是我參看的一位關于深拷貝的問題解決。

JSON.parse

先將一個對象轉為json對象。然后再解析這個json對象。

let obj = {a:{b:22}};let copy = JSON.parse(JSON.stringify(obj));

這種方法的優點就是代碼寫起來比較簡單。但是缺點也是顯而易見的。你先是創建一個臨時的,可能很大的字符串,只是為了把它重新放回解析器。另一個缺點是這種方法不能處理循環對象。

如下面的循環對象用這種方法的時候會拋出異常

let a = {};let b = {a};a.b = b;let copy = JSON.parse(JSON.stringify(a));

深拷貝怎么實現

諸如 Map, Set, RegExp, Date, ArrayBuffer 和其他內置類型在進行序列化時會丟失。

let a = {};let b = new Set();b.add(11);a.test = b;let copy = JSON.parse(JSON.stringify(a));

a 的值打印如下

深拷貝怎么實現

copy的值打印如下

深拷貝怎么實現

對比發現,Set已丟失。

Structured Clone 結構化克隆算法

MessageChannel

建立兩個端,一個端發送消息,另一個端接收消息。

function structuralClone(obj) {    return new Promise(resolve =>{
        const {port1, port2} = new MessageChannel();
        port2.onmessage = ev => resolve(ev.data);
        port1.postMessage(obj);
    })
}
const obj = /* ... */;
structuralClone(obj).then(res=>{
     console.log(res);
})

這種方法的優點就是能解決循環引用的問題,還支持大量的內置數據類型。缺點就是這個方法是異步的。

History API

利用history.replaceState。這個api在做單頁面應用的路由時可以做無刷新的改變url。這個對象使用結構化克隆,而且是同步的。但是我們需要注意,在單頁面中不要把原有的路由邏輯搞亂了。所以我們在克隆完一個對象的時候,要恢復路由的原狀。

function structuralClone(obj) {
  const oldState = history.state;
  history.replaceState(obj, document.title);
  const copy = history.state;
  history.replaceState(oldState, document.title);  return copy;
}var obj = {};var b = {obj};
obj.b = bvar copy = structuralClone(obj); 
console.log(copy);

這個方法的優點是。能解決循環對象的問題,也支持許多內置類型的克隆。并且是同步的。但是缺點就是有的瀏覽器對調用頻率有限制。比如Safari 30 秒內只允許調用 100 次

Notification API

這個api主要是用于桌面通知的。如果你使用Facebook的時候,你肯定會發現時常在瀏覽器的右下角有一個彈窗,對就是這家伙。我們也可以利用這個api實現js對象的深拷貝。

function structuralClone(obj) {  return new Notification('', {data: obj, silent: true}).data;
}var obj = {};var b = {obj};
obj.b = bvar copy = structuralClone(obj);
console.log(copy)

同樣是優點和缺點并存,優點就是可以解決循環對象問題,也支持許多內置類型的克隆,并且是同步的。缺點就是這個需要api的使用需要向用戶請求權限,但是用在這里克隆數據的時候,不經用戶授權也可以使用。在http協議的情況下會提示你再https的場景下使用。

lodash的_.cloneDeep()

支持循環對象,和大量的內置類型,對很多細節都處理的比較不錯。推薦使用。

支持的類型有很多

深拷貝怎么實現

我們這里再次關注一下lodash是如何解決循環應用這個問題的?

深拷貝怎么實現

從相關的代碼中。我們可以發現。lodash是用一個棧記錄了。所有被拷貝的引用值。如果再次碰到同樣的引用值的時候,不會再去拷貝一遍。而是利用之前已經拷貝好的值。

實現一個簡易點的深拷貝,以解決循環引用的問題為目標

我們僅僅實現一個簡易點的深拷貝。能優雅的處理循環引用的即可。在實現深拷貝之前,我們首先溫習回顧一下js中的遍歷對象的屬性的方法和各種方法的優缺點。

js中遍歷一個對象的屬性的方法

  • Object.keys() 僅僅返回自身的可枚舉屬性,不包括繼承來的,更不包括Symbol屬性

  • Object.getOwnPropertyNames() 返回自身的可枚舉和不可枚舉屬性。但是不包括Symbol屬性

  • Object.getOwnPropertySymbols() 返回自身的Symol屬性

  • for...in 可以遍歷對象的自身的和繼承的可枚舉屬性,不包含Symbol屬性

  • Reflect.ownkeys() 返回對象自身的所有屬性,不管是否可枚舉,也不管是否是Symbol。注意不包括繼承的屬性

實現深拷貝,解決循環引用問題

/**
 * 判斷是否是基本數據類型
 * @param value 
 */function isPrimitive(value){  return (typeof value === 'string' || 
  typeof value === 'number' || 
  typeof value === 'symbol' ||  typeof value === 'boolean')
}/**
 * 判斷是否是一個js對象
 * @param value 
 */function isObject(value){  return Object.prototype.toString.call(value) === "[object Object]"}/**
 * 深拷貝一個值
 * @param value 
 */function cloneDeep(value){  // 記錄被拷貝的值,避免循環引用的出現
  let memo = {};  function baseClone(value){
    let res;    // 如果是基本數據類型,則直接返回
    if(isPrimitive(value)){      return value;    // 如果是引用數據類型,我們淺拷貝一個新值來代替原來的值
    }else if(Array.isArray(value)){
      res = [...value];
    }else if(isObject(value)){
      res = {...value};
    }    // 檢測我們淺拷貝的這個對象的屬性值有沒有是引用數據類型。如果是,則遞歸拷貝
    Reflect.ownKeys(res).forEach(key=>{      if(typeof res[key] === "object" && res[key]!== null){        //此處我們用memo來記錄已經被拷貝過的引用地址。以此來解決循環引用的問題
        if(memo[res[key]]){
          res[key] = memo[res[key]];
        }else{
          memo[res[key]] = res[key];
          res[key] = baseClone(res[key])
        }
      }
    })    return res;  
  }  return baseClone(value)
}

驗證我們寫的cloneDeep是否能解決循環應用的問題

var obj = {};var b = {obj};
obj.b = bvar copy = cloneDeep(obj); 
console.log(copy);

以上是“深拷貝怎么實現”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

腾冲县| 浮梁县| 昔阳县| 专栏| 大庆市| 枣强县| 兴隆县| 民丰县| 阿尔山市| 浪卡子县| 五家渠市| 郧西县| 含山县| 乌苏市| 东乡县| 平顺县| 横峰县| 惠水县| 温宿县| 饶河县| 资溪县| 光山县| 共和县| 花莲县| 沙田区| 内丘县| 阿克陶县| 阿荣旗| 秦皇岛市| 额尔古纳市| 响水县| 泉州市| 建平县| 临泽县| 华宁县| 南木林县| 雅安市| 津市市| 行唐县| 班戈县| 同江市|