<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【每日一面】實(shí)現(xiàn)一個(gè)深拷貝函數(shù)

      基礎(chǔ)問(wèn)答

      問(wèn):知道淺拷貝和深拷貝嗎?為什么要用深拷貝?

      答:拷貝,可以認(rèn)為是賦值,對(duì)于 JavaScript 中的基礎(chǔ)類(lèi)型,如 string, number, null, boolean, undefined, symbol 等,在賦值給一個(gè)變量的時(shí)候,是直接拷貝值給變量,而對(duì)于引用類(lèi)型,如 object, array, function 等,則會(huì)拷貝其引用(地址)。
      使用深拷貝,是為了避免操作公共對(duì)象的時(shí)候,影響到其他使用該對(duì)象的組件。

      擴(kuò)展延伸

      一個(gè)拷貝函數(shù),可以直接評(píng)估出來(lái)你對(duì) JavaScript 基礎(chǔ)能力掌握水平。

      在理解淺拷貝和深拷貝前,需先明確拷貝的本質(zhì)。在 JavaScript 中數(shù)據(jù)類(lèi)型分為基本類(lèi)型(string、number、boolean、null、undefined、symbol、bigint)和引用類(lèi)型(object、array、function 等),這兩種類(lèi)型在內(nèi)存中存儲(chǔ)方式是不一樣的:

      • 基本類(lèi)型:值直接存儲(chǔ)在棧內(nèi)存中,賦值時(shí)直接拷貝值。
      • 引用類(lèi)型:值存儲(chǔ)在堆內(nèi)存中,棧內(nèi)存僅存儲(chǔ)指向堆內(nèi)存的引用地址,賦值時(shí)僅拷貝引用地址(而非實(shí)際值)。

      所以,根據(jù)這兩種存儲(chǔ)方式很容易想到,淺拷貝和深拷貝的區(qū)別就在于 是否遞歸復(fù)制嵌套的引用類(lèi)型。 這里給出一個(gè)簡(jiǎn)單的定義:

      • 淺拷貝(Shallow Copy):僅復(fù)制對(duì)象的表層屬性,若屬性值為引用類(lèi)型(如嵌套對(duì)象、數(shù)組),則拷貝的是引用地址(引用地址就是表層屬性),新舊對(duì)象共享嵌套數(shù)據(jù)。
      • 深拷貝(Deep Copy):遞歸復(fù)制對(duì)象的所有屬性,包括嵌套的引用類(lèi)型,新舊對(duì)象完全獨(dú)立,修改拷貝后的對(duì)象不會(huì)影響原始對(duì)象的數(shù)據(jù)。

      實(shí)現(xiàn)方式

      淺拷貝

      淺拷貝適用于無(wú)嵌套引用類(lèi)型或無(wú)需獨(dú)立嵌套數(shù)據(jù)的場(chǎng)景,實(shí)現(xiàn)方式簡(jiǎn)單,性能開(kāi)銷(xiāo)小。

      1. 淺拷貝對(duì)象 Object.assign()
        Object.assign(target, ...sources) 方法將源對(duì)象的可枚舉屬性復(fù)制到目標(biāo)對(duì)象,最后返回的是目標(biāo)對(duì)象,使用這個(gè)方法時(shí)要注意:該方法僅拷貝對(duì)象自身屬性(不包含繼承屬性),嵌套的對(duì)象僅拷貝引用,示例如下:
      const obj = { a: 1, b: { c: 2 } };
      const shallowCopy = Object.assign({}, obj);
      
      // 測(cè)試基本類(lèi)型屬性:修改不影響原對(duì)象
      shallowCopy.a = 100;
      console.log(obj.a); // 輸出:1(原對(duì)象不變)
      
      // 測(cè)試嵌套對(duì)象:修改會(huì)影響原對(duì)象
      shallowCopy.b.c = 200;
      console.log(obj.b.c); // 輸出:200(原對(duì)象被修改)
      
      1. 淺拷貝數(shù)組 Array.prototype.slice()Array.prototype.concat()
        這兩個(gè)方法返回的都是新數(shù)組(不在原數(shù)組上操作),示例如下:
      const arr = [1, [2, 3]];
      const shallowCopy1 = arr.slice(0); // 方法1:slice
      const shallowCopy2 = [].concat(arr); // 方法2:concat
      
      // 測(cè)試基本類(lèi)型元素:修改不影響原數(shù)組
      shallowCopy1[0] = 100;
      console.log(arr[0]); // 輸出:1(原數(shù)組不變)
      
      // 測(cè)試嵌套數(shù)組:修改會(huì)影響原數(shù)組
      shallowCopy2[1][0] = 200;
      console.log(arr[1][0]); // 輸出:200(原數(shù)組被修改)
      
      1. 擴(kuò)展運(yùn)算符 ...
        這個(gè)是 es6 新增的運(yùn)算符,可以用于對(duì)象和數(shù)組的淺拷貝,語(yǔ)法相較于上面兩種方式比較簡(jiǎn)單,示例如下:
      // 對(duì)象淺拷貝
      const obj = { a: 1, b: { c: 2 } };
      const shallowObj = { ...obj };
      
      // 數(shù)組淺拷貝
      const arr = [1, [2, 3]];
      const shallowArr = [...arr];
      

      深拷貝

      深拷貝適用于包含嵌套引用類(lèi)型且需要完全獨(dú)立副本的場(chǎng)景,實(shí)現(xiàn)復(fù)雜度較高,需處理遞歸、循環(huán)引用等邊界情況。屬于前端八股面試必須準(zhǔn)備的一個(gè)問(wèn)題。

      1. 序列化方式拷貝 JSON.parse(JSON.stringify())
        利用 JSON 序列化與反序列化實(shí)現(xiàn)深拷貝,語(yǔ)法簡(jiǎn)單,多數(shù)時(shí)候夠用。
      const obj = { a: 1, b: { c: 2 }, d: [3, 4] };
      const deepCopy = JSON.parse(JSON.stringify(obj));
      
      // 測(cè)試嵌套對(duì)象:修改不影響原對(duì)象
      deepCopy.b.c = 200;
      console.log(obj.b.c); // 輸出:2(原對(duì)象不變)
      

      但是這個(gè)方式有一定的局限性:

      • 不能拷貝函數(shù)(JSON不支持)
      • 不能拷貝 undefinedSymbol 類(lèi)型
      • 不能處理循環(huán)引用
      • 不支持 BigInt 類(lèi)型
      • 對(duì)于日期對(duì)象和正則對(duì)象,有特殊處理,解析后可能得不到我們想要的結(jié)果
      1. 自定義實(shí)現(xiàn)拷貝函數(shù)
        思路:遍歷對(duì)象,每一次遍歷過(guò)程中判斷是否是引用類(lèi)型(對(duì)象或數(shù)組),如果是,則遞歸的調(diào)用拷貝函數(shù),若不是,則直接賦值進(jìn)行下一步。
      function deepCopy(target) {
        // 基本類(lèi)型直接返回
        if (target === null || typeof target !== 'object') {
          return target;
        }
      
        // 區(qū)分?jǐn)?shù)組和對(duì)象
        let copy;
        if (Array.isArray(target)) {
          copy = [];
        } else {
          copy = {};
        }
      
        // 遍歷屬性并遞歸拷貝
        for (const key in target) {
          if (target.hasOwnProperty(key)) {
            // 遞歸處理引用類(lèi)型
            copy[key] = deepCopy(target[key]);
          }
        }
      
        return copy;
      }
      
      // 測(cè)試
      const obj = { a: 1, b: { c: 2 }, d: [3, 4] };
      const copyObj = deepCopy(obj);
      copyObj.b.c = 200;
      console.log(obj, copyObj, obj === copyObj, obj.b.c); // 對(duì)比輸出結(jié)果,可以發(fā)現(xiàn)兩個(gè)對(duì)象是不同的
      copyObj.d[0] = 300;
      console.log(obj, copyObj, obj === copyObj, obj.d[0]); // 同上
      

      但是這個(gè)沒(méi)有處理邊界情況,主要是兩種情況:

      • 循環(huán)應(yīng)用
        循環(huán)引用指對(duì)象引用自身(如 obj.self = obj),直接遞歸會(huì)導(dǎo)致無(wú)限循環(huán)棧溢出。可以用 WeakMap 存儲(chǔ)已拷貝的對(duì)象,避免在遞歸過(guò)程中重復(fù)拷貝。

      • 特殊對(duì)象
        類(lèi)似于 Date,RegExp 的對(duì)象,需要我們手動(dòng)特殊處理(根據(jù)類(lèi)型直接 new)
        完整的深拷貝示例:

      function deepCopy(target, hash = new WeakMap()) {
        // 基本類(lèi)型直接返回
        if (target === null || typeof target !== 'object') {
          return target;
        }
      
        // 處理循環(huán)引用:若已拷貝過(guò),直接返回緩存的副本
        if (hash.has(target)) {
          return hash.get(target);
        }
      
        let copy;
      
        // 處理Date
        if (target instanceof Date) {
          copy = new Date(target);
          hash.set(target, copy);
          return copy;
        }
      
        // 處理RegExp
        if (target instanceof RegExp) {
          copy = new RegExp(target.source, target.flags);
          copy.lastIndex = target.lastIndex; // 保留lastIndex屬性
          hash.set(target, copy);
          return copy;
        }
      
        // 處理數(shù)組和對(duì)象
        if (Array.isArray(target)) {
          copy = [];
        } else {
          // 處理普通對(duì)象(包括自定義對(duì)象)
          copy = new target.constructor(); // 保持原型鏈
        }
      
        // 緩存已拷貝的對(duì)象,解決循環(huán)引用
        hash.set(target, copy);
      
        // 遍歷屬性并遞歸拷貝
        // 處理Map
        if (target instanceof Map) {
          target.forEach((value, key) => {
            copy.set(key, deepCopy(value, hash));
          });
          return copy;
        }
      
        // 處理Set
        if (target instanceof Set) {
          target.forEach(value => {
            copy.add(deepCopy(value, hash));
          });
          return copy;
        }
      
        // 處理普通對(duì)象和數(shù)組的屬性
        for (const key in target) {
          if (target.hasOwnProperty(key)) {
            copy[key] = deepCopy(target[key], hash);
          }
        }
      
        return copy;
      }
      
      // 測(cè)試循環(huán)引用
      const obj = { name: 'test' };
      obj.self = obj; // 循環(huán)引用
      const copyObj = deepCopy(obj);
      console.log(copyObj.self === copyObj, copyObj === obj, obj, copyObj);
      
      // 測(cè)試特殊對(duì)象
      const date = new Date();
      const copyDate = deepCopy(date);
      console.log(copyDate instanceof Date, copyDate === date, date, copyDate);
      
      const reg = /abc/gim;
      reg.lastIndex = 10;
      const copyReg = deepCopy(reg);
      console.log(copyReg, reg);
      

      差異對(duì)比

      這里我簡(jiǎn)單總結(jié)一個(gè)表來(lái)讓你快速理解二者異同:

      對(duì)比方向 淺拷貝 深拷貝
      拷貝層級(jí) 僅拷貝對(duì)象表層屬性 遞歸拷貝所有層級(jí)(包括嵌套的引用類(lèi)型)
      內(nèi)存占用 較小(共享嵌套對(duì)象的內(nèi)存) 較大(完全復(fù)制所有數(shù)據(jù),獨(dú)立占用內(nèi)存)
      性能開(kāi)銷(xiāo) 低(無(wú)需遞歸,操作簡(jiǎn)單) 高(遞歸處理,需處理邊界情況)
      拷貝前后對(duì)象的獨(dú)立性 表層屬性獨(dú)立,嵌套引用類(lèi)型共享 完全獨(dú)立,新舊對(duì)象無(wú)任何關(guān)聯(lián)
      適用場(chǎng)景 無(wú)嵌套引用類(lèi)型、性能優(yōu)先、無(wú)需獨(dú)立嵌套數(shù)據(jù)的情況,簡(jiǎn)單來(lái)說(shuō),不需要前后獨(dú)立的,都可以直接用淺拷貝 有嵌套引用類(lèi)型、需完全隔離數(shù)據(jù)、修改不能相互影響的情況
      實(shí)現(xiàn)復(fù)雜度 簡(jiǎn)單(可通過(guò)原生方法或簡(jiǎn)單遍歷實(shí)現(xiàn)) 復(fù)雜(需處理遞歸、循環(huán)引用、特殊對(duì)象類(lèi)型)

      面試追問(wèn)

      1. 直接使用 = 賦值算淺拷貝還是深拷貝?
        都不是,賦值運(yùn)算符只是將一個(gè)值或者引用賦給一個(gè)變量,對(duì)于基本類(lèi)型,賦值運(yùn)算符是直接復(fù)制這個(gè)值給變量,對(duì)于引用類(lèi)型,賦值運(yùn)算符則是復(fù)制引用給變量,而非對(duì)象本身。
        這個(gè)和淺拷貝的定義略有差異。

      2. 實(shí)現(xiàn)一個(gè)淺拷貝函數(shù)?
        思路就是,直接遍歷淺層對(duì)象(第一層),賦給新的對(duì)象。

      function shallowCopy(target) {
        // 區(qū)分目標(biāo)是數(shù)組還是對(duì)象
        if (Array.isArray(target)) {
          const copy = [];
          for (let i = 0; i < target.length; i++) {
            copy[i] = target[i];
          }
          return copy;
        } else if (target !== null && typeof target === 'object') {
          const copy = {};
          // 僅拷貝自身可枚舉屬性
          for (const key in target) {
            if (target.hasOwnProperty(key)) {
              copy[key] = target[key];
            }
          }
          return copy;
        } else {
          // 基本類(lèi)型直接返回(無(wú)需拷貝)
          return target;
        }
      }
      
      // 測(cè)試
      const obj = { a: 1, b: { c: 2 }, d: [3, 4] };
      const copyObj = shallowCopy(obj);
      copyObj.b.c = 200;
      console.log(obj.b.c); // 輸出:200(嵌套對(duì)象共享引用)
      
      1. 深拷貝的時(shí)候,怎么特殊處理函數(shù)類(lèi)型?
        函數(shù)屬于引用類(lèi)型,通常不需要深拷貝,因?yàn)楹瘮?shù)體是改不了的,通常直接復(fù)制引用就行了。
        如果面試時(shí)強(qiáng)烈要求你深拷貝,可以直接使用 toString() + eval 實(shí)現(xiàn),但可能隨之而來(lái)的會(huì)將話題轉(zhuǎn)到 eval 上來(lái)問(wèn)詞法作用域、嚴(yán)格模式、安全問(wèn)題等等,一般是來(lái)轉(zhuǎn)換個(gè)話題。

      2. 實(shí)際開(kāi)發(fā)的時(shí)候,有經(jīng)常用這兩種模式嗎?舉個(gè)場(chǎng)景說(shuō)明一下

      • 前端分頁(yè),displayData 通常是直接通過(guò) slice 獲取原始列表的一部分?jǐn)?shù)據(jù),由于不需要操作,所以也不需要深拷貝
      • 接口傳參,有時(shí)候我們?yōu)榱朔奖悖瑫?huì)在請(qǐng)求數(shù)據(jù)信息之后,直接將這個(gè)返回的對(duì)象賦值給某個(gè)地方,之后再提交的時(shí)候,由于接口要求的信息不同,我們有可能會(huì)直接操作這個(gè)返回對(duì)象,導(dǎo)致使用返回對(duì)象的地方出現(xiàn)變化,這種情況就需要深拷貝。
      posted @ 2025-11-03 21:19  Achieve前端實(shí)驗(yàn)室  閱讀(149)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 无码人妻av免费一区二区三区| 亚洲女同在线播放一区二区| 麻豆国产传媒精品视频| 动漫AV纯肉无码AV电影网| 重口SM一区二区三区视频| 一本之道高清乱码少妇| 亚洲综合欧美在线…| 国产精品制服丝袜白丝| 亚洲深深色噜噜狠狠网站| 一本久道中文无码字幕av| 久久精品99国产精品亚洲| 欧美丰满熟妇bbbbbb| 午夜DY888国产精品影院| 天堂网av一区二区三区| 中文一区二区视频| 极品人妻少妇一区二区三区| 一本久道中文无码字幕av| 亚洲全网成人资源在线观看| 女人的天堂A国产在线观看| 日韩中文字幕国产精品| 国产一区二区精品偷系列| 久久精品国产99精品国产2021| 中文字幕久区久久中文字幕| 三上悠亚久久精品| 长沙县| 亚洲免费最大黄页网站| 国产成人无码免费看视频软件| 欧美成人精品三级在线观看 | 日日碰狠狠躁久久躁96avv| 亚洲综合色区另类av| 夏邑县| 国产二区三区不卡免费| 日本久久香蕉一本一道| 亚洲国产精品乱码一区二区| 野花韩国高清电影| 成年女人免费视频播放体验区 | 亚洲成人av在线系列| 无码人妻斩一区二区三区| 曰韩精品无码一区二区三区视频| 国产区成人精品视频| 亚洲性日韩精品一区二区三区|