亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JavaScript 實現一個響應式系統的解決方案

 更新時間:2024年04月25日 09:15:32   作者:jiang_xin_yu  
這篇文章主要介紹了JavaScript 實現一個響應式系統的解決方案,本次示例使用Proxy實現數據監(jiān)控,結合實例代碼給大家介紹的非常詳細,需要的朋友可以參考下

第一階段目標

  • 數據變化重新運行依賴數據的過程

第一階段問題

  • 如何知道數據發(fā)生了變化
  • 如何知道哪些過程依賴了哪些數據

第一階段問題的解決方案

  • 我們可用參考現有的響應式系統(vue)

    vue2 是通過 Object.defineProperty實現數據變化的監(jiān)控,詳細查看 Vue2官網。

    vue3 是通過Proxy實現數據變化的監(jiān)控,詳細查看 Vue3官網。

  • 本次示例使用Proxy實現數據監(jiān)控,Proxy詳細信息查看官網。
  • 根據解決方案,需要改變第一階段目標為-> Proxy對象變化重新運行依賴數據的過程
  • 問題變更->如何知道Proxy發(fā)生了變化
  • 問題變更->如何知道哪些函數依賴了哪些Proxy

如何知道 Proxy 對象發(fā)生了變化,示例代碼

//這里傳入一個對象,返回一個Proxy對象,對Proxy對象的屬性的讀取和修改會觸發(fā)內部的get,set方法
function relyOnCore(obj) {
  if (typeof obj !== "object" || obj === null) {
    return obj;
  }
  return new Proxy(obj, {
    get(target, key, receiver) {
      return target[key];
    },
    set(target, key, value, receiver) {
      //這里需要返回是否修改成功的Boolean值
      return Reflect.set(target, key, value);
    },
  });
}

數據監(jiān)控初步完成,但是這里只監(jiān)控了屬性的讀取和設置,還有很多操作沒有監(jiān)控,以及數據的 this 指向,我們需要完善它

//完善后的代碼
export function relyOnCore(obj) {
  if (typeof obj !== "object" || obj === null) {
    return obj;
  }
  return new Proxy(obj, {
    get(target, key, receiver) {
      if (typeof target[key] === "object" && target[key] !== null) {
        //當讀取的值是一個對象,需要重新代理這個對象
        return relyOnCore(target[key]);
      }
      return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver) {
      return Reflect.set(target, key, value, receiver);
    },
    ownKeys(target) {
      return Reflect.ownKeys(target);
    },
    getOwnPropertyDescriptor(target, key) {
      return Reflect.getOwnPropertyDescriptor(target, key);
    },
    has(target, p) {
      return Reflect.has(target, p);
    },
    deleteProperty(target, key) {
      return Reflect.deleteProperty(target, key);
    },
    defineProperty(target, key, attributes) {
      return Reflect.defineProperty(target, key, attributes);
    },
  });
}

如何知道哪些函數依賴了哪些 Proxy 對象

問題:依賴 Proxy 對象的函數要如何收集

在收集依賴 Proxy 對象的函數的時候出現了一個問題: 無法知道數據在什么環(huán)境使用的,拿不到對應的函數

解決方案

既然是因為無法知道函數的執(zhí)行環(huán)境導致的無法找到對應函數,那么我們只需要給函數一個固定的運行環(huán)境就可以知道函數依賴了哪些數據。

示例

//定義一個變量
export let currentFn;
export function trackFn(fn) {
  return function FnTrackEnv() {
    currentFn = FnTrackEnv;
    fn();
    currentFn = null;
  };
}

自此,我們的函數調用期間 Proxy 對象監(jiān)聽到的數據讀取在 currentFn 函數內部發(fā)生的。

同樣,我們的目標從最開始的 數據變化重新運行依賴數據的過程 -> Proxy 對象變化重新運行依賴收集完成的函數

完善函數調用環(huán)境

直接給全局變量賦值,在函數嵌套調用的情況下,這個依賴收集,會出現問題

let obj1 = relyOnCore({ a: 1, b: 2, c: { d: 3 } });
function fn1() {
  let a = obj1.a;
  function fn2() {
    let b = obj1.b;
  }
  //這里的c會無法收集依賴
  let c = obj1.c;
}

我們修改一下函數收集

export const FnStack = [];
export function trackFn(fn) {
  return function FnTrackEnv() {
    FnStack.push(FnTrackEnv);
    fn();
    FnStack.pop(FnTrackEnv);
  };
}

第二階段目標

  • 在合適的時機觸發(fā)合適的函數

第二階段問題

  • 在什么時間觸發(fā)函數
  • 到達觸發(fā)時間時,應該觸發(fā)什么函數

第一個問題:在什么時間觸發(fā)函數

必然是在修改數據完成之后觸發(fā)函數

第二個問題:應該觸發(fā)什么函數

當操作會改變函數讀取的信息的時候,需要重新運行函數。因此,我們需要建立一個映射關系

{
  //對象
  "obj": {
    //屬性
    "key": {
      //對屬性的操作
      "handle": ["fn"] //對應的函數
    }
  }
}

在數據改變的時候,我們只需要根據映射關系,循環(huán)運行 handle 內的函數

數據讀取和函數建立聯系

我們可以創(chuàng)建一個函數用于建立這種聯系

export function track(object, handle, key, fn) {}

這個函數接收 4 個參數,object(對象),handle(對數據的操作類型) key(操作了對象的什么屬性),fn(需要關聯的函數) 

我們現在來創(chuàng)建映射關系

export const ObjMap = new WeakMap();
export const handleType = {
  GET: "GET",
  SET: "SET",
  Delete: "Delete",
  Define: "Define",
  Has: "Has",
  getOwnPropertyDescriptor: "getOwnPropertyDescriptor",
  ownKeys: "ownKeys",
};
export function track(object, handle, key, fn) {
  setObjMap(object, key, handle, fn);
}
function setObjMap(obj, key, handle, fn) {
  if (!ObjMap.has(obj)) {
    ObjMap.set(obj, new Map());
  }
  setKeyMap(obj, key, handle, fn);
}
const setKeyMap = (obj, key, handle, fn) => {
  let keyMap = ObjMap.get(obj);
  if (!keyMap.has(key)) {
    keyMap.set(key, new Map());
  }
  setHandle(obj, key, handle, fn);
};
const setHandle = (obj, key, handle, fn) => {
  let keyMap = ObjMap.get(obj);
  let handleMap = keyMap.get(key);
  if (!handleMap.has(handle)) {
    handleMap.set(handle, new Set());
  }
  setFn(obj, key, handle, fn);
};
const setFn = (obj, key, handle, fn) => {
  let keyMap = ObjMap.get(obj);
  let handleMap = keyMap.get(key);
  let fnSet = handleMap.get(handle);
  fnSet.add(fn);
};

現在已經實現了數據和函數之間的關聯只需要在讀取數據時調用這個方法去收集依賴就可以,代碼如下:

export function relyOnCore(obj) {
  if (typeof obj !== "object" || obj === null) {
    return obj;
  }
  return new Proxy(obj, {
    get(target, key, receiver) {
      track(target, handleType.GET, key, FnStack[FnStack.length - 1]);
      if (typeof target[key] === "object" && target[key] !== null) {
        return relyOnCore(target[key]);
      }
      return Reflect.get(target, key, receiver);
    },
    //....這里省略剩余代碼
  });
}

接下來我們需要建立數據改變->影響哪些數據的讀取之間的關聯

export const TriggerToTrackMap = new Map([
  [handleType.SET, [handleType.GET, handleType.getOwnPropertyDescriptor]],
  [
    handleType.Delete,
    [
      handleType.GET,
      handleType.ownKeys,
      handleType.Has,
      handleType.getOwnPropertyDescriptor,
    ],
  ],
  [handleType.Define, [handleType.ownKeys, handleType.Has]],
]);

建立這樣關聯后,我們只需要在數據變動的時候,根據映射關系去尋找需要重新運行的函數就可以實現響應式。

export function trigger(object, handle, key) {
  let keyMap = ObjMap.get(object);
  if (!keyMap) {
    return;
  }
  let handleMap = keyMap.get(key);
  if (!handleMap) {
    return;
  }
  let TriggerToTrack = TriggerToTrackMap.get(handle);
  let fnSet = new Set();
  TriggerToTrack.forEach((handle) => {
    let fnSetChiren = handleMap.get(handle);
    if (fnSetChiren) {
      fnSetChiren.forEach((fn) => {
        if (fn) {
          fnSet.add(fn);
        }
      });
    }
  });
  fnSet.forEach((fn) => {
    fn();
  });
}

總結

以上簡易的實現了響應式系統,只是粗略的介紹了如何實現,會存在一些 bug

到此這篇關于JavaScript 如何實現一個響應式系統的文章就介紹到這了,更多相關JavaScript 響應式系統內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • javascript dragable的Move對象

    javascript dragable的Move對象

    一個dragable的Move對象,大家可以運行下,測試看下效果。
    2009-08-08
  • js簡單的分頁器插件代碼實例

    js簡單的分頁器插件代碼實例

    這篇文章主要介紹了js簡單的分頁器插件代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-09-09
  • JavaScript實現div的鼠標拖拽效果

    JavaScript實現div的鼠標拖拽效果

    這篇文章主要為大家詳細介紹了JavaScript實現div的鼠標拖拽效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • 微信小程序中換行空格(多個空格)寫法詳解

    微信小程序中換行空格(多個空格)寫法詳解

    這篇文章主要介紹了微信小程序中換行空格(多個空格)寫法詳解,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-07-07
  • 解決input輸入框僅支持輸入數字及兩位小數點的限制

    解決input輸入框僅支持輸入數字及兩位小數點的限制

    這篇文章主要為大家介紹了解決input輸入框僅支持輸入數字及兩位小數點的限制技巧示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-11-11
  • 使用Promise進行異步處理的操作步驟

    使用Promise進行異步處理的操作步驟

    在JavaScript中,異步操作是非常常見的,如網絡請求、文件操作、定時任務等,Promise是一種用于管理異步操作的解決方案,它使得異步代碼變得更易讀、易于組合和錯誤處理更加集中,本文將詳細介紹如何使用Promise進行錯誤處理,需要的朋友可以參考下
    2025-03-03
  • for?of?和?for?in?的區(qū)別介紹

    for?of?和?for?in?的區(qū)別介紹

    這篇文章主要介紹了for?of?和?for?in?的區(qū)別,for?of?和?for?in都是用來遍歷的屬性,本文重點介紹下for?of?和?for?in?的區(qū)別,需要的朋友可以參考下
    2022-12-12
  • 詳解如何編寫一個Typescript的類型聲明文件

    詳解如何編寫一個Typescript的類型聲明文件

    我們知道TypeScript根據類型聲明進行類型檢查,但有些情況可能沒有類型聲明,這個時候就需要我們自己寫一個,下面小編就來和大家聊聊如果寫一個Typescript的類型聲明文件呢
    2023-06-06
  • webpack-cli在webpack打包中的作用小結

    webpack-cli在webpack打包中的作用小結

    webpack?是打包代碼時依賴的核心內容,而?webpack-cli?是一個用來在命令行中運行?webpack?的工具,那么webpack-cli在webpack打包中的作用是什么,本文就詳細的介紹一下,感興趣的可以了解一下
    2022-04-04
  • javascript 數字格式化輸出的實現代碼

    javascript 數字格式化輸出的實現代碼

    這篇文章主要是對javascript中數字格式化輸出的實現代碼進行了介紹,需要的朋友可以過來參考下,希望對大家有所幫助
    2013-12-12

最新評論