自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Javascript的對象拷貝

開發(fā) 前端
在開始之前,我先普及一些基礎(chǔ)知識。Javascript 的對象只是指向內(nèi)存中某個位置的指針。這些指針是可變的,也就是說,它們可以重新被賦值。

[[391560]]

在開始之前,我先普及一些基礎(chǔ)知識。Javascript 的對象只是指向內(nèi)存中某個位置的指針。這些指針是可變的,也就是說,它們可以重新被賦值。所以僅僅復(fù)制這個指針,其結(jié)果是有兩個指針指向內(nèi)存中的同一個地址。

  1. var foo = { 
  2.     a : "abc" 
  3. console.log(foo.a); 
  4. // abc 
  5.  
  6. var bar = foo
  7. console.log(bar.a); 
  8. // abc 
  9.  
  10. foo.a = "yo foo"
  11. console.log(foo.a); 
  12. // yo foo 
  13. console.log(bar.a); 
  14. // yo foo 
  15.  
  16. bar.a = "whatup bar?"
  17. console.log(foo.a); 
  18. // whatup bar? 
  19. console.log(bar.a); 
  20. // whatup bar?     

通過上面的例子可以看到,對象 foo 和 bar 都能隨著對方的變化而變化。所以在拷貝 Javascript 中的對象時,要根據(jù)實際情況做一些考慮。

淺拷貝

如果要操作的對象擁有的屬性都是值類型,那么可以使用擴(kuò)展語法或 Object.assign(...)

  1. var obj = { foo: "foo", bar: "bar" }; 
  2. var copy = { ...obj }; 
  3. // Object { foo: "foo", bar: "bar" } 
  4. var obj = { foo: "foo", bar: "bar" }; 
  5. var copy = Object.assign({}, obj); 
  6. // Object { foo: "foo", bar: "bar" } 

可以看到上面兩種方法都可以把多個不同來源對象中的屬性復(fù)制到一個目標(biāo)對象中。

  1. var obj1 = { foo: "foo" }; 
  2. var obj2 = { bar: "bar" }; 
  3. var copySpread = { ...obj1, ...obj2 }; 
  4. // Object { foo: "foo", bar: "bar" } 
  5. var copyAssign = Object.assign({}, obj1, obj2); 
  6. // Object { foo: "foo", bar: "bar" } 

上面這種方法是存在問題的,如果對象的屬性也是對象,那么實際被拷貝的只是那些指針,這跟執(zhí)行 var bar = foo; 的效果是一樣的,和第一段代碼中的做法一樣。

  1. var foo = { a: 0 , b: { c: 0 } }; 
  2. var copy = { ...foo }; 
  3. copy.a = 1
  4. copy.b.c = 2
  5. console.dir(foo); 
  6. // { a: 0, b: { c: 2 } } 
  7. console.dir(copy); 
  8. // { a: 1, b: { c: 2 } } 

深拷貝(有限制)

想要對一個對象進(jìn)行深拷貝,一個可行的方法是先把對象序列化為字符串,然后再對它進(jìn)行反序列化。

  1. var obj = { a: 0, b: { c: 0 } }; 
  2. var copy = JSON.parse(JSON.stringify(obj)); 

不幸的是,這個方法只在對象中包含可序列化值,同時沒有循環(huán)引用的情況下適用。常見的不能被序列化的就是日期對象 —— 盡管它顯示的是字符串化的 ISO 日期格式,但是 JSON.parse 只會把它解析成為一個字符串,而不是日期類型。

深拷貝 (限制較少)

對于一些更復(fù)雜的場景,我們可以用 HTML5 提供的一個名為結(jié)構(gòu)化克隆的新算法。不過,截至本文發(fā)布為止,有些內(nèi)置類型仍然無法支持,但與 JSON.parse 相比較而言,它支持的類型要多的多:Date、RegExp、 Map、 Set、 Blob、 FileList、 ImageData、 sparse 和 typed Array。它還維護(hù)了克隆對象的引用,這使它可以支持循環(huán)引用結(jié)構(gòu)的拷貝,而這些在前面所說的序列化中是不支持的。

目前還沒有直接調(diào)用結(jié)構(gòu)化克隆的方法,但是有些新的瀏覽器特性的底層用了這個算法。所以深拷貝對象可能需要依賴一系列的環(huán)境才能實現(xiàn)。

Via MessageChannels: 其原理是借用了通信中用到的序列化算法。由于它是基于事件的,所以這里的克隆也是一個異步操作。

  1. class StructuredCloner { 
  2.   constructor() { 
  3.     this.pendingClones_ = new Map(); 
  4.     this.nextKey_ = 0
  5.  
  6.     const channel = new MessageChannel(); 
  7.     this.inPort_ = channel.port1; 
  8.     this.outPort_ = channel.port2; 
  9.  
  10.     this.outPort_.onmessage = ({data: {key, value}}) => { 
  11.       const resolve = this.pendingClones_.get(key); 
  12.       resolve(value); 
  13.       this.pendingClones_.delete(key); 
  14.     }; 
  15.     this.outPort_.start(); 
  16.   } 
  17.  
  18.   cloneAsync(value) { 
  19.     return new Promise(resolve => { 
  20.       const key = this.nextKey_++; 
  21.       this.pendingClones_.set(key, resolve); 
  22.       this.inPort_.postMessage({key, value}); 
  23.     }); 
  24.   } 
  25.  
  26. const structuredCloneAsync = window.structuredCloneAsync = 
  27.     StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner); 
  28.  
  29. const main = async () => { 
  30.   const original = { date: new Date(), number: Math.random() }; 
  31.   originaloriginal.self = original; 
  32.  
  33.   const clone = await structuredCloneAsync(original); 
  34.  
  35.   // different objects: 
  36.   console.assert(original !== clone); 
  37.   console.assert(original.date !== clone.date); 
  38.  
  39.   // cyclical: 
  40.   console.assert(original.self === original); 
  41.   console.assert(clone.self === clone); 
  42.  
  43.   // equivalent values: 
  44.   console.assert(original.number === clone.number); 
  45.   console.assert(Number(original.date) === Number(clone.date)); 
  46.  
  47.   console.log("Assertions complete."); 
  48. }; 
  49.  
  50. main(); 

Via the history API:history.pushState() 和 history.replaceState()都會給它們的第一個參數(shù)做一個結(jié)構(gòu)化克隆!需要注意的是,此方法是同步的,因為對瀏覽器歷史記錄進(jìn)行操作的速度不是很快,假如頻繁調(diào)用這個方法,將會導(dǎo)致瀏覽器卡死。

  1. const structuredClone = obj => { 
  2.   const oldState = history.state; 
  3.   history.replaceState(obj, null); 
  4.   const clonedObj = history.state; 
  5.   history.replaceState(oldState, null); 
  6.   return clonedObj; 
  7. }; 

Via notification API:當(dāng)創(chuàng)建一個 notification 實例的時候,構(gòu)造器為它相關(guān)的數(shù)據(jù)做了結(jié)構(gòu)化克隆。需要注意的是,它會嘗試向用戶展示瀏覽器通知,但是除非它收到了用戶允許展示通知的請求,否則它什么都不會做。一旦用戶點擊同意的話,notification 會立刻被關(guān)閉。

  1. const structuredClone = obj => { 
  2.   const n = new Notification("", {data: obj, silent: true}); 
  3.   nn.onshow = n.close.bind(n); 
  4.   return n.data; 
  5. }; 

用 Node.js 進(jìn)行深拷貝

Node.js 的 8.0.0 版本提供了一個 序列化 api 可以和結(jié)構(gòu)化克隆相媲美. 不過這個 API 在本文發(fā)布的時候,還只是被標(biāo)記為試驗性的:

  1. const v8 = require('v8'); 
  2. const buf = v8.serialize({a: 'foo', b: new Date()}); 
  3. const cloned = v8.deserialize(buf); 
  4. cloned.b.getMonth(); 

在 8.0.0 版本以下比較穩(wěn)定的方法,可以考慮用 lodash 的 cloneDeep函數(shù),它的思想多少也基于結(jié)構(gòu)化克隆算法。

結(jié)論

Javascript 中最好的對象拷貝的算法,很大程度上取決于其使用環(huán)境,以及你需要拷貝的對象類型。雖然 lodash 是最安全的泛型深拷貝函數(shù),但是如果你自己封裝的話,也許能夠獲得效率更高的實現(xiàn)方法,以下就是一個簡單的深拷貝,對 Date 日期對象也同樣適用:

  1. function deepClone(obj) { 
  2.   var copy; 
  3.  
  4.   // Handle the 3 simple types, and null or undefined 
  5.   if (null == obj || "object" != typeof obj) return obj; 
  6.  
  7.   // Handle Date 
  8.   if (obj instanceof Date) { 
  9.     copy = new Date(); 
  10.     copy.setTime(obj.getTime()); 
  11.     return copy; 
  12.   } 
  13.  
  14.   // Handle Array 
  15.   if (obj instanceof Array) { 
  16.     copy = []; 
  17.     for (var i = 0len = obj.length; i < len; i++) { 
  18.         copy[i] = deepClone(obj[i]); 
  19.     } 
  20.     return copy; 
  21.   } 
  22.  
  23.   // Handle Function 
  24.   if (obj instanceof Function) { 
  25.     copy = function() { 
  26.       return obj.apply(this, arguments); 
  27.     } 
  28.     return copy; 
  29.   } 
  30.  
  31.   // Handle Object 
  32.   if (obj instanceof Object) { 
  33.       copy = {}; 
  34.       for (var attr in obj) { 
  35.           if (obj.hasOwnProperty(attr)) copy[attr] = deepClone(obj[attr]); 
  36.       } 
  37.       return copy; 
  38.   } 
  39.  
  40.   throw new Error("Unable to copy obj as type isn't supported " + obj.constructor.name); 

我很期待可以隨便使用結(jié)構(gòu)化克隆的那一天的到來,讓對象拷貝不再令人頭疼^_^

 

責(zé)任編輯:趙寧寧 來源: 前端先鋒
相關(guān)推薦

2025-04-27 09:45:58

JavaScript深拷貝淺拷貝

2024-08-02 08:43:24

JavaScript開發(fā)者工具箱深拷貝

2021-07-16 12:33:24

Javascript深拷貝淺拷貝

2017-05-24 11:54:55

Javascript深拷貝

2020-10-12 08:35:22

JavaScript

2018-09-26 14:37:17

JavaScript前端編程語言

2022-09-26 09:01:23

JavaScript淺拷貝深拷貝

2018-05-10 14:20:18

前端JavaScript深拷貝

2021-05-14 00:00:15

JavaScript開發(fā)代碼

2020-06-23 08:41:47

JavaScript開發(fā)技術(shù)

2023-05-08 09:00:46

JSON深拷貝對象

2023-05-12 08:11:58

JavaScriptJSON克隆

2024-01-29 00:57:20

GuavaJava拷貝

2023-05-16 07:35:29

2021-10-21 18:47:37

JavaScript面向對象

2021-07-27 22:56:00

JavaScript編程開發(fā)

2011-07-01 10:52:49

C++構(gòu)造函數(shù)賦值操作

2023-05-05 08:47:35

Java淺拷貝深拷貝

2023-05-17 07:36:00

淺拷貝深拷貝對象

2020-05-18 10:52:00

前端JavaScript html
點贊
收藏

51CTO技術(shù)棧公眾號