成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

前端百題斬—賦值、淺拷貝、深拷貝大PK

開發(fā) 前端
淺拷貝指的就是循環(huán)遍歷對象一遍,將該對象上的屬性賦值到另一個對象上。在這個過程中屬性值為基本類型則拷貝的就是基本類型的值;若該值為引用類型,則拷貝的就是就是一個內(nèi)存地址。

[[429471]]

寫該系列文章的初衷是“讓每位前端工程師掌握高頻知識點(diǎn),為工作助力”。

相信老鐵們不管是在學(xué)習(xí)還是面試過程中,都會遇到賦值、淺拷貝、深拷貝,特別是淺拷貝和深拷貝,我記憶比較深刻的遇到這個問題有兩次:

一次系統(tǒng)寫出bug就是因?yàn)閷ι顪\拷貝理解不清楚;

百度面試。

1 賦值

賦值指的就是將一個變量直接賦值給另一個變量,如下所示:

  1. const a1 = 10; 
  2. const a2 = a1; 
  3. console.log(a2); // 10 
  4.  
  5. const b1 = { 
  6.     m: 10, 
  7.     n: 20 
  8. }; 
  9.  
  10. const b2 = b1; 
  11. console.log(b2); // { m: 10, n: 20 } 

如上所示,賦值就是將一個值賦給另一個值,在賦值過程中要注意兩點(diǎn):

  1. 對于基本類型賦值就是在棧內(nèi)存中開辟一個新的存儲區(qū)域來存儲新的變量;
  2. 對于引用類型賦值,就是將該引用類型的地址,該地址指向堆中的同一值。

2 淺拷貝

2.1 基本實(shí)現(xiàn)

淺拷貝指的就是循環(huán)遍歷對象一遍,將該對象上的屬性賦值到另一個對象上。在這個過程中屬性值為基本類型則拷貝的就是基本類型的值;若該值為引用類型,則拷貝的就是就是一個內(nèi)存地址。

  1. function clone(source) { 
  2.     if (!(typeof source === 'object' && source !== null)) { 
  3.         return source; 
  4.     } 
  5.     const target = {}; // 只考慮Object類型 
  6.     for (let [key, value] of Object.entries(source)) { 
  7.         target[key] = value; 
  8.     } 
  9.  
  10.     return target; 
  11.  
  12. const obj = { 
  13.     a: 10, 
  14.     b: { 
  15.         m: 20 
  16.     } 
  17. }; 
  18.  
  19. const cloneObj = clone(obj); 
  20.  
  21. cloneObj.a = 20; 
  22. cloneObj.b.m = 30; 
  23.  
  24. console.log(obj); // { a: 10, b: { m: 30 } } 
  25. console.log(cloneObj); // { a: 20, b: { m: 30 } } 

上述就是簡單的淺拷貝過程,可以看到淺拷貝就是將原始對象中的值遍歷一層,然后賦值給一個新的對象。在遍歷過程中可以獲取到一下信息:

  1. 遍歷到a屬性的時(shí)候,其是一個基本類型,所以會在棧內(nèi)存中創(chuàng)建一個新的存儲區(qū)域來存儲變量。
  2. 遍歷到b屬性的時(shí)候,由于其為引用類型,其會在棧內(nèi)存中存儲器堆地址,從而指向堆內(nèi)存中的同一對象。
  3. 當(dāng)通過淺拷貝創(chuàng)建的對象cloneObj中的a屬性和b.m屬性重新賦值,可以發(fā)現(xiàn)a屬性值不一樣,但b.m屬性值卻發(fā)生了變化,從而驗(yàn)證了上述1、2兩條分析。

2.2 進(jìn)階

既然本章我們講了淺拷貝,那么不得不了解Object.assign(),該方法就是一個淺拷貝的過程,用于對象的合并,將源對象(source)的所有可枚舉屬性,復(fù)制到目標(biāo)對象(target)。

2.2.1 基礎(chǔ)

要實(shí)現(xiàn)一個函數(shù)首先應(yīng)該了解一個函數(shù),對于該方法的基本使用就不再贅述,下面主要講幾個注意點(diǎn):

  1. 如果目標(biāo)對象與源對象有同名屬性(或多個源對象有同名屬性),則后面的屬性會覆蓋前面的屬性;
  2. 如果只有一個參數(shù),Object.assign會直接返回該參數(shù)。如果該參數(shù)不是對象,則會先轉(zhuǎn)為對象,然后再返回;(注意:由于undefined和null無法轉(zhuǎn)為對象,將它們作為參數(shù)會報(bào)錯)
  3. 非對象參數(shù)出現(xiàn)在源對象位置,這些參數(shù)會轉(zhuǎn)化為對象,如果無法轉(zhuǎn)成對象便跳過(所以undefined和null不會報(bào)錯)。(注意:字符串會以數(shù)組形式復(fù)制到目標(biāo)對象,其它不會)
  4. 只復(fù)制源對象的自身屬性(不復(fù)制繼承屬性),也不復(fù)制不可枚舉的屬性;
  5. 屬性名為Symbol值的屬性也會被Object.assign復(fù)制。

2.2.2 實(shí)現(xiàn)

上面闡述了主要的注意點(diǎn),下面我們就來實(shí)現(xiàn)一下Object.assign(),實(shí)現(xiàn)步驟如下所示:

  1. 對目標(biāo)對象進(jìn)行判斷,不能為null和undefined;
  2. 將目標(biāo)轉(zhuǎn)換為對象(防止string、number等);
  3. 獲取后續(xù)源對象自身中的可枚舉對象(包含Symbol)復(fù)制到目標(biāo)對象;
  4. 返回該處理好的目標(biāo)對象;
  5. 利用Object.defineProperty()將該函數(shù)配置為不可枚舉的掛載到Object上。
  1. function ObjectAssign(target, ...sources) { 
  2.     // 對第一個參數(shù)進(jìn)行判斷,不能為undefined和null 
  3.     if (target === undefined || target === null) { 
  4.         throw new TypeError('cannot convert first argument to object'); 
  5.     } 
  6.  
  7.     // 將第一個參數(shù)轉(zhuǎn)換為對象 
  8.     const targetObj = Object(target); 
  9.     // 將源對象(source)自身的所有可枚舉屬性復(fù)制到目標(biāo)對象(target) 
  10.     for (let i = 0; i < sources.length; i++) { 
  11.         let source = sources[i]; 
  12.         // 對于undefined和null在源對象中不會報(bào)錯,會直接跳過 
  13.         if (source !== undefined && source !== null) { 
  14.             // 將源角色轉(zhuǎn)換成對象 
  15.             // 需要將源角色自身的可枚舉屬性(包含Symbol值的屬性)進(jìn)行復(fù)制 
  16.             // Reflect.ownKeys(obj)  返回一個數(shù)組,包含對象自身的所有屬性,不管屬性名是Symbol還是字符串,也不管是否可枚舉 
  17.             const keysArrays = Reflect.ownKeys(Object(source)); 
  18.             for (let nextIndex = 0; nextIndex < keysArrays.length; nextIndex++) { 
  19.                 const nextKey = keysArrays[nextIndex]; 
  20.                 // 去除不可枚舉屬性 
  21.                 const desc = Object.getOwnPropertyDescriptor(source, nextKey); 
  22.                 if (desc !== undefined && desc.enumerable) { 
  23.                     targetObj[nextKey] = source[nextKey]; 
  24.                 } 
  25.             } 
  26.         } 
  27.     } 
  28.  
  29.     return targetObj; 
  30.  
  31. // 由于掛載到Object的assign是不可枚舉的,直接掛載上去是可枚舉的,所以采用這種方式 
  32. if (typeof Object.myAssign !== 'function') { 
  33.     Object.defineProperty(Object, "myAssign", { 
  34.         value: ObjectAssign, 
  35.         writable: true
  36.         enumerable: false
  37.         configurable: true 
  38.     }); 
  39.  
  40. const target = { 
  41.     a: 10 
  42. }; 
  43. const source1 = { 
  44.     b: 20, 
  45.     c: 30 
  46. }; 
  47. const source2 = { 
  48.     c: 40 
  49. }; 
  50.  
  51. console.log(Object.assign(target, source1, source2)); // { a: 10, b: 20, c: 40 } 
  52. console.log(Object.myAssign(target, source1, source2)); // { a: 10, b: 20, c: 40 } 

3 深拷貝

深拷貝其實(shí)就是淺拷貝的進(jìn)階版,因?yàn)闇\拷貝只循環(huán)遍歷了一層數(shù)據(jù),對于引用類型拷貝的是對象的地址,但是深拷貝會進(jìn)行多層的遍歷,將所有數(shù)據(jù)進(jìn)行數(shù)據(jù)層面的拷貝。下面就利用三種方式實(shí)現(xiàn)深拷貝。(這篇文章寫得很好,大家可以一起看一下)

3.1 乞丐版

首先來看一下最簡單的深拷貝方式,就是利用JSON.stringify()和JSON.parse(),但是該方式其實(shí)是存在很多問題的:

  1. 不能正確處理正則表達(dá)式,其會變?yōu)榭諏ο?
  2. 不能正確處理函數(shù),其變?yōu)閡ndefined;
  3. 不能正常輸出值為undefined的內(nèi)容。
  1. function cloneDeep(source) { 
  2.     return JSON.parse(JSON.stringify(source)); 
  3.  
  4. const obj = { 
  5.     a: 10, 
  6.     b: undefined, 
  7.     c: /\w/g, 
  8.     d: function() { 
  9.         return true
  10.     } 
  11. }; 
  12. console.log(obj); // { a: 10, b: undefined, c: /\w/g, d: [Function: d] } 
  13. console.log(cloneDeep(obj)); // { a: 10, c: {} } 

3.2 遞歸版

既然乞丐版有這么多問題,那么就嘗試一下“淺拷貝+遞歸”的方式實(shí)現(xiàn)一下。

  1. function cloneDeep(source) { 
  2.     // 如果輸入的為基本類型,直接返回 
  3.     if (!(typeof source === 'object' && source !== null)) { 
  4.         return source; 
  5.     } 
  6.  
  7.     // 判斷輸入的為數(shù)組還是對象,進(jìn)行對應(yīng)的創(chuàng)建 
  8.     const target = Array.isArray(source) ? [] : {}; 
  9.      
  10.     for (let [key, value] of Object.entries(source)) { 
  11.         // 此處應(yīng)該去除一些內(nèi)置對象,根據(jù)需要可以自己去除,本初只去除了RegExp對象 
  12.         if (typeof value === 'object' && value !== null && !(value instanceof RegExp)) { 
  13.             target[key] = cloneDeep(value); 
  14.         } 
  15.         else { 
  16.             target[key] = value; 
  17.         } 
  18.     } 
  19.  
  20.     return target; 
  21.  
  22. const obj = { 
  23.     a: 10, 
  24.     b: undefined, 
  25.     c: /\w/g, 
  26.     d: function() { 
  27.         return true
  28.     }, 
  29.     e: { 
  30.         m: 20, 
  31.         n: 30 
  32.     } 
  33. }; 
  34. const result = cloneDeep(obj); 
  35.  
  36. result.e.m = 100; 
  37.  
  38. console.log('拷貝前:', obj); 
  39. console.log('拷貝后:', result); 

輸出結(jié)果如下所示:

3.3 循環(huán)方式

利用遞歸的方式實(shí)現(xiàn)深拷貝,其實(shí)是存在爆棧的風(fēng)險(xiǎn)的,下面就將遞歸的方式改為循環(huán)的方式。

  1. // 循環(huán)方式 
  2. function cloneDeep(source) { 
  3.     if (!(typeof source === 'object' && source !== null)) { 
  4.         return source; 
  5.     } 
  6.  
  7.     const root = Array.isArray(source) ? [] : {}; 
  8.     // 定義一個棧 
  9.     const loopList = [{ 
  10.         parent: root, 
  11.         key: undefined, 
  12.         data: source, 
  13.     }]; 
  14.  
  15.     while (loopList.length > 0) { 
  16.         // 深度優(yōu)先 
  17.         const node = loopList.pop(); 
  18.         const parent = node.parent; 
  19.         const key = node.key
  20.         const data = node.data; 
  21.  
  22.         // 初始化賦值目標(biāo),key為undefined則拷貝到父元素,否則拷貝到子元素 
  23.         let res = parent; 
  24.         if (typeof key !== 'undefined') { 
  25.             res = parent[key] = Array.isArray(data) ? [] : {}; 
  26.         } 
  27.  
  28.         for (let [childKey, value] of Object.entries(data)) { 
  29.             if (typeof value === 'object' && value !== null && !(value instanceof RegExp)) { 
  30.                 loopList.push({ 
  31.                     parent: res, 
  32.                     key: childKey, 
  33.                     data: value 
  34.                 }); 
  35.             } else { 
  36.                 res[childKey] = value; 
  37.             } 
  38.         } 
  39.     } 
  40.  
  41.     return root; 
  42.  
  43. const obj = { 
  44.     a: 10, 
  45.     b: undefined, 
  46.     c: /\w/g, 
  47.     d: function() { 
  48.         return true
  49.     }, 
  50.     e: { 
  51.         m: 20, 
  52.         n: 30 
  53.     } 
  54. }; 
  55. const result = cloneDeep(obj); 
  56.  
  57. result.e.m = 100; 
  58.  
  59. console.log('拷貝前:', obj); 
  60. console.log('拷貝后:', result); 

輸出結(jié)果如下所示:

 

責(zé)任編輯:姜華 來源: 前端點(diǎn)線面
相關(guān)推薦

2021-06-28 07:12:28

賦值淺拷貝深拷貝

2022-09-30 15:03:09

C語言深拷貝淺拷貝

2021-01-08 06:15:09

深拷貝淺拷貝寫時(shí)拷貝

2023-05-17 08:42:46

深拷貝Golang

2021-07-16 12:33:24

Javascript深拷貝淺拷貝

2017-08-16 13:30:05

Java深拷貝淺拷貝

2020-10-12 08:35:22

JavaScript

2021-09-27 11:07:11

深拷貝淺拷貝內(nèi)存

2024-03-15 15:03:23

2022-07-26 08:07:03

Python淺拷貝深拷貝

2009-05-19 17:28:44

深拷貝淺拷貝clone()

2020-08-03 08:24:26

原型模式拷貝

2024-02-05 22:56:16

C++拷貝開發(fā)

2020-06-23 08:41:47

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

2021-09-10 07:41:06

Python拷貝Python基礎(chǔ)

2018-09-26 14:37:17

JavaScript前端編程語言

2024-04-17 09:01:08

Python深拷貝淺拷貝

2019-02-25 08:58:16

Python深拷貝淺拷貝

2018-05-10 14:20:18

前端JavaScript深拷貝

2023-05-17 07:36:00

淺拷貝深拷貝對象
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 天天色综网 | 亚洲欧美精品在线 | 国产美女精品 | 91人人看 | 日韩在线不卡 | 亭亭五月激情 | 欧美精选一区二区 | 性视频网 | 在线观看成年人视频 | 日屁网站 | 成人免费视频在线观看 | 亚洲一二三区精品 | 久久久久久国模大尺度人体 | 日韩在线视频免费观看 | 日韩福利视频 | 国产精品国产三级国产aⅴ原创 | 国产视频1| 欧美日韩在线综合 | 羞羞视频免费观看入口 | 免费观看成人性生生活片 | 亚洲精品毛片av | 日一区二区 | 欧美大片一区二区 | 成人免费视频在线观看 | 美女一区二区在线观看 | 欧美日本一区二区 | 91视在线国内在线播放酒店 | 99精品亚洲国产精品久久不卡 | h在线观看 | 亚洲综合五月天婷婷 | 欧美激情久久久 | 国产精品久久久久久久久久久久久久 | 在线欧美视频 | 韩国主播午夜大尺度福利 | 亚洲 欧美 综合 | 久久香蕉精品视频 | 中文字幕91 | 午夜欧美一区二区三区在线播放 | 亚洲成a| 中文字幕一区二区三区四区不卡 | 国产精品久久久久久久久久久久久 |