首頁 > 軟體

Proxy的不可變資料優點及使用詳解

2023-03-02 18:01:05

可變資料

  • 物件被賦值後,更改物件,兩個都會改變,因為其參照著相同的地址,我們稱這為可變物件
  • 所以這會造成意想不到的修改
  • React 要求本地元件的狀態保持不可變性,Redux 同樣要求全域性狀態保持不可變

不可變資料(Immutable Data)

  • Immutable.js 中 對 Immutable 物件 增刪改查都會返回一個全新的 Immutable 物件,保證舊資料的可用不變
  • Immutable 使用了結構共用,即物件樹中的節點改變只會影響自己和其父節點,其他節點共用

優點

  • 保護資料意外更改,減少bug
  • 方便跟蹤資料變更,便於排錯

實現一:獨立方法

  • 呼叫麻煩
function updateData(obj, key, value) {
  return {
    ...obj,
    [key]: value
  };
}
const obj = {
  name: "雲牧"
};
const newObj = updateData(obj, "name", "黛玉");
console.log(newObj); // { name: '黛玉' }
console.log(obj); // { name: '雲牧' }

實現二:自定義物件

  • 自定義新的物件型別,對其操作細節封裝在其內部
  • 著名的 immutable-js,就是這個思路,定義了ListStackMapOrderedMapSetOrderedSetRecord 這麼多物件
class MyObject {
  constructor(obj = {}) {
    this.obj = { ...obj };
  }
  get(name) {
    return this.obj[name];
  }
  set(name, value) {
    return new MyObject({
      ...this.obj,
      [name]: value
    });
  }
}
const obj = new MyObject({
  name: "雲牧"
});
const newObj = obj.set("name", "黛玉");
console.log(newObj); // MyObject { obj: { name: '黛玉' } }
console.log(obj); // MyObject { obj: { name: '雲牧' } }

實現三:函數 + 複製

  • 函數呼叫產生新物件,對其新物件操作之後返回
  • 效能損耗比較大
function produce(obj, recipe) {
  const newObj = { ...obj };
  recipe(newObj);
  return newObj;
}
const obj = {
  name: "雲牧"
};
const newObj = produce(obj, draft => {
  draft.name = "黛玉";
});
console.log(newObj); // { name: '黛玉' }
console.log(obj); // { name: '雲牧' }

實現四:Proxy代理

function produce(obj, recipe) {
  const state = {
    base: obj, // 基礎物件
    copy: {}, // 被更改後的物件
    draft: {}, // 代理物件
    currentKey: 0 // 當前操作的key
  };
  const handlerItem = {
    get(target, property, receiver) {
      // 如果更改後的物件存在則使用copy
      if (state.copy[state.currentKey]) {
        return state.copy[state.currentKey][property];
      }
      return state.base[state.currentKey][property];
    },
    set(target, property, value, receiver) {
      Reflect.set(state.copy[state.currentKey], property, value);
    }
  };
  const handler = {
    get(target, property, receiver) {
      state.currentKey = property;
      if (!state.draft[property]) {
        const val = { ...state.base[property] };
        const proxy = new Proxy(val, handlerItem);
        state.draft[property] = proxy;
        state.copy[property] = val;
      }
      return state.draft[property];
    },
    set(target, property, value, receiver) {
      return Reflect.set(state.copy, property, value);
    }
  };
  const proxyObj = new Proxy(obj, handler);
  recipe(proxyObj);
  return proxyObj;
}
const arrObj = Array.from({ length: 100 }, (v, index) => ({ name: "雲牧" + index }));
const newObj = produce(arrObj, draft => {
  draft[50].name = "黛玉";
});
console.log(newObj[50].name); // 黛玉
console.log(arrObj[50].name); // 雲牧50

實現五:第三方不可變物件

  • JS沒有不可變結構,我們一般可以使用 Immutable.jsimmerjs
  • Immutable.js 需要學習他的資料格式操作,且其不可變資料需要 toJS 才能得到原生物件,心智負擔大
  • immerjs 則沒有這方面的問題,且體積更為小巧

以上就是Proxy的不可變資料優點及使用詳解的詳細內容,更多關於Proxy不可變資料的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com