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

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

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

      [JS] 深拷貝的實現

      淺拷貝和深拷貝的區別

      • 淺拷貝:淺拷貝指的是復制一個對象的時候,對于對象的一個屬性,
        • 如果是基本數據類型,則復制其值;
        • 如果是引用數據類型,則復制其引用。
      • 深拷貝:深拷貝指的是復制一個對象的時候,對于對象的一個屬性,
        • 如果是基本數據類型,則復制其值;
        • 如果是引用數據類型,則遞歸地深拷貝該對象。

      從內存的堆區和棧區上觀察它們的區別:

      • 淺拷貝:引用數據類型的屬性值指向同一個對象實例:

        image-20240716165302305

      • 深拷貝:引用數據類型的屬性值在深拷貝的時候會在堆區申請新的空間,然后填入新內容(遞歸調用):

        image-20240716165429589

      如何實現深拷貝

      接口定義:deepClone(sourceObject): newObject

      需要注意或者思考的問題:

      1. 小心循環引用導致的無限遞歸導致棧溢出;

        可以使用一個WeakMap記錄已創建的對象,后續遞歸過程中如果引用了該對象,則直接填入其引用,而不是遞歸調用deepClone,從而避免了無限遞歸。

        其中WeakMap<source, target>用于記錄源對象上的引用到目標對象上的引用的映射記錄。因為在deepClone的過程中,我們是在使用DFS遍歷source的過程中構建target

      2. 遞歸調用什么時候返回?

        • 基本數據類型直接返回;
        • 如果WeakMap上已經存在記錄,說明存在循環引用,直接返回記錄的引用,而不是遞歸調用。
      3. 如何獲取對象上的key,常規的key、Symbol類型的key、不可遍歷的key如何獲取?

        直接使用Reflect.ownKeys可以獲取對象上的常規key、Symbol-key和不可遍歷的key。

      4. 拷貝對象的時候,對象屬性的描述符也要復制;

        使用Reflect.getOwnPropertyDescriptor(target, key)方法可以獲取target對象上key屬性的描述符對象。

      5. 對于特殊類型的引用數據類型,應考慮對應的復制方法,如SetMap數據類型需要考慮添加記錄的順序

      如何判斷引用數據類型

      可以使用typeof判斷一個變量是否是object類型,使用typeof需要注意兩個特例:

      1. typeof null === 'object'null是基本數據類型,但是會返回object;
      2. typeof function(){} === 'function',function是引用數據類型且是object的子類型,但是會返回function
      function isObject(target){
        return (typeof target==='object' && target!==null) || typeof target==='function';
      }
      

      代碼

      function deepClone(target){
        // 提前記錄clone的鍵值對,用于處理循環引用
        const map = new WeakMap();
      
        /**
         * 輔助函數:判斷是否是對象類型  
         * 需要注意`null` 和 `function`
         * @returns 
         */
        function isObject(target){
          return (typeof target === 'object' && target !== null)
          || typeof target === 'function'
        }
        function clone(target){
          /**
           * 基本數據類型
           * 操作:直接返回
           */
          if(!isObject(target))return target;
      
          /**
           * Date和RegExp對象類型
           * 操作:使用構造函數復制
           */
          if([Date, RegExp].includes(target.constructor)){
            return new target.constructor(target);
          }
      
          /**
           * 函數類型
           */
          if(typeof target==='function'){
            return new Function('return ' + target.toString())();
          }
      
          /**
           * 數組類型
           */
          if(Array.isArray(target)){
            return target.map(el => clone(el));
          }
      
          /**
           * 檢查是否存在循環引用
           */
          if(map.has(target))return map.get(target);
      
          /**
           * 處理Map對象類型
           */
          if(target instanceof Map){
            const res = new Map();
            map.set(target, res);
            target.forEach((val, key) => {
              // 如果Map中的val是對象,也得深拷貝
              res.set(key, isObject(val) ? clone(val) : val);
            })
            return res;
          }
      
          /**
           * 處理Set對象類型
           */
          if(target instanceof Set){
            const res = new Set();
            map.set(target, res);
            target.forEach(val => {
              // 如果val是對象類型,則遞歸深拷貝
              res.add(isObject(val) ? clone(val) : val);
            })
            return res;
          }
      
          //==========================================
          // 接下來是常規對象類型
          //==========================================
          
          // 收集key(包括Symbol和不可枚舉的屬性)
          const keys = Reflect.ownKeys(target);
          // 收集各個key的描述符
          const allDesc = {};
          keys.forEach(key => {
            allDesc[key] = Reflect.getOwnPropertyDescriptor(target, key);
          })
          // 創建新對象(淺拷貝)
          const res = Reflect.construct(Reflect.getPrototypeOf(target).constructor, []);
          // 在遞歸調用clone之前記錄新對象,避免循環
          map.set(target, res);
          // 賦值并檢查是否val是否為對象類型
          keys.forEach(key => {
            // 添加對象描述符
            Reflect.defineProperty(res, key, allDesc[key]);
            // 賦值
            const val = target[key];
            res[key] = isObject(val) ? clone(val) : val;
          });
          return res;
        }
      
        return clone(target);
      }
      

      使用jest測試

      安裝jest

      pnpm install jest --save-dev
      

      這里我使用的版本是:

      {
          ...
          "devDependencies": {
          	"jest": "^29.7.0"  
          },
          ...
      }
      

      指令

      package.json

      {
          ...
          "scripts": {
              "test": "jest"
          },
          ...
      }
      

      編寫測試用例

      deepClone.test.js

      const deepClone = require('./deepClone');
      
      test('deep clone primitive types', () => {
        expect(deepClone(42)).toBe(42);
        expect(deepClone('hello')).toBe('hello');
        expect(deepClone(null)).toBeNull();
        expect(deepClone(undefined)).toBeUndefined();
        expect(deepClone(true)).toBe(true);
      });
      
      test('deep clone array', () => {
        const arr = [1, { a: 2 }, [3, 4]];
        const clonedArr = deepClone(arr);
        expect(clonedArr).toEqual(arr);
        expect(clonedArr).not.toBe(arr);
        expect(clonedArr[1]).not.toBe(arr[1]);
        expect(clonedArr[2]).not.toBe(arr[2]);
      });
      
      test('deep clone object', () => {
        const obj = { a: 1, b: { c: 2 } };
        const clonedObj = deepClone(obj);
        expect(clonedObj).toEqual(obj);
        expect(clonedObj).not.toBe(obj);
        expect(clonedObj.b).not.toBe(obj.b);
      });
      
      test('deep clone Map', () => {
        const map = new Map();
        map.set('a', 1);
        map.set('b', { c: 2 });
        const clonedMap = deepClone(map);
        expect(clonedMap).toEqual(map);
        expect(clonedMap).not.toBe(map);
        expect(clonedMap.get('b')).not.toBe(map.get('b'));
      });
      
      test('deep clone Set', () => {
        const obj1 = { a: 1 };
        const obj2 = { b: 2 };
        const set = new Set([1, 'string', obj1, obj2]);
        const clonedSet = deepClone(set);
        expect(clonedSet).toEqual(set);
        expect(clonedSet).not.toBe(set);
        expect(clonedSet.has(1)).toBe(true);
        expect(clonedSet.has('string')).toBe(true);
        const clonedObj1 = Array.from(clonedSet).find(item => typeof item === 'object' && item.a === 1);
        const clonedObj2 = Array.from(clonedSet).find(item => typeof item === 'object' && item.b === 2);
      
        expect(clonedObj1).toEqual(obj1);
        expect(clonedObj1).not.toBe(obj1);
      
        expect(clonedObj2).toEqual(obj2);
        expect(clonedObj2).not.toBe(obj2);
      });
      
      test('deep clone with Symbol keys', () => {
        const sym = Symbol('key');
        const obj = { [sym]: 1, a: 2 };
        const clonedObj = deepClone(obj);
        expect(clonedObj).toEqual(obj);
        expect(clonedObj[sym]).toBe(1);
        expect(clonedObj.a).toBe(2);
      });
      
      test('deep clone with non-enumerable properties', () => {
        const obj = {};
        Object.defineProperty(obj, 'a', { value: 1, enumerable: false });
        const clonedObj = deepClone(obj);
        expect(clonedObj).toHaveProperty('a', 1);
        expect(Object.keys(clonedObj)).not.toContain('a');
      });
      
      test('deep clone with property descriptors', () => {
        const obj = {};
        Object.defineProperty(obj, 'a', {
          value: 1,
          writable: false,
          configurable: false,
          enumerable: true
        });
        const clonedObj = deepClone(obj);
        const desc = Object.getOwnPropertyDescriptor(clonedObj, 'a');
        expect(desc.value).toBe(1);
        expect(desc.writable).toBe(false);
        expect(desc.configurable).toBe(false);
        expect(desc.enumerable).toBe(true);
      });
      
      test('deep clone circular references', () => {
        const obj = { a: 1 };
        obj.self = obj;
        const clonedObj = deepClone(obj);
        expect(clonedObj).toEqual(obj);
        expect(clonedObj.self).toBe(clonedObj);
        expect(clonedObj.self).not.toBe(obj);
      });
      
      

      測試結果

      npm run test
      

      image-20240716175612883

      posted @ 2024-07-16 18:23  feixianxing  閱讀(199)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲综合精品一区二区三区| 被喂春药蹂躏的欲仙欲死视频| 无码一区二区三区免费| 午夜福利在线观看6080| 美女内射福利大全在线看| 久久精品一本到99热免费| 宾馆人妻4P互换视频| 福利一区二区视频在线| 亚洲无线观看国产精品| 变态另类视频一区二区三区| 日韩一区在线中文字幕| 日韩欧美人妻一区二区三区| 亚洲精品一区二区18禁| 人人妻人人狠人人爽天天综合网 | 中文有无人妻vs无码人妻激烈| 国产欧美va欧美va在线| 乱人伦中文视频在线| 亚洲性日韩精品一区二区| 国产乱女乱子视频在线播放| 色猫咪av在线网址| 欧美一区二区三区成人久久片| 亚洲日本欧洲二区精品| 久草热8精品视频在线观看| 日韩幕无线码一区中文| 一区二区免费高清观看国产丝瓜| 国产成人免费观看在线视频| 国产乱色熟女一二三四区| 丰满少妇人妻久久久久久| 国产av熟女一区二区三区| 午夜通通国产精品福利| 中文人妻av高清一区二区| 精品亚洲欧美高清不卡高清| 亚洲欧洲精品国产二码| 在线观看无码av五月花| 国精一二二产品无人区免费应用| 国产一级区二级区三级区| 无码AV无码免费一区二区| 爱啪啪精品一区二区三区| 亚洲一品道一区二区三区| 香蕉久久精品日日躁夜夜躁夏| 日韩精品一卡二卡在线观看|