国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁技術文章
文章詳情頁

JS帶你深入領略Proxy的世界

瀏覽:75日期:2024-03-26 08:48:50
目錄1. Proxy 的基本結構2. Proxy 與 Reflect3. 代理數組4. 代理函數5. 一些簡單的應用場景5.1 統計函數被調用的上下文和次數5.2 實現一個防抖功能5.3 實現觀察者模式6. Proxy 與 Object.defineProperty 的對比6.1 Object.defineProperty 的優(yōu)劣6.2 Proxy 的優(yōu)劣7. 總結1. Proxy 的基本結構

Proxy 的基本使用方式:

/** * target: 表示要代理的目標,可以是object, array, function類型 * handler: 是一個對象,可以編寫各種代理的方法 */const proxy = new Proxy(target, handler);

例如我們想要代理一個對象,可以通過設置 get 和 set 方法來代理獲取和設置數據的操作:

const person = { name: ’wenzi’, age: 20,};const personProxy = new Proxy(person, { get(target, key, receiver) { console.log(`get value by ${key}`); return target[key]; }, set(target, key, value) { console.log(`set ${key}, old value ${target[key]} to ${value}`); target[key] = value; },});

Proxy 僅僅是一個代理,personProxy 上有 person 所有的屬性和方法。我們通過personProxy獲取和設置 name 時,就會有相應的 log 輸出:

personProxy.name; // 'wenzi'// log: get value by namepersonProxy.name = ’hello’;// log: set name, old value wenzi to hello

并且通過 personProxy 設置數據時,代理的原結構里的數據也會發(fā)生變化。我們打印下 person,可以發(fā)現字段 name 的值 也變成了hello:

console.log(person); // {name: 'hello', age: 20}

Proxy 的第 2 個參數 handler 除了可以設置 get 和 set 方法外,還有更多豐富的方法:

1.get(target, propKey, receiver):攔截對象屬性的讀取,比如 proxy.foo 和 proxy[’foo’]。

2.set(target, propKey, value, receiver):攔截對象屬性的設置,比如 proxy.foo = v 或 proxy[’foo’] = v,返回一個布爾值。

3.has(target, propKey):攔截 propKey in proxy 的操作,返回一個布爾值。

4.deleteProperty(target, propKey):攔截 delete proxy[propKey]的操作,返回一個布爾值。

5.ownKeys(target):攔截 Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in 循環(huán),返回一個數組。該方法返回目標對象所有自身的屬性的屬性名,而 Object.keys()的返回結果僅包括目標對象自身的可遍歷屬性。

6.getOwnPropertyDescriptor(target, propKey):攔截 Object.getOwnPropertyDescriptor(proxy, propKey),返回屬性的描述對象。

7.defineProperty(target, propKey, propDesc):攔截 Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一個布爾值。

8.preventExtensions(target):攔截 Object.preventExtensions(proxy),返回一個布爾值。

9.getPrototypeOf(target):攔截 Object.getPrototypeOf(proxy),返回一個對象。

10.isExtensible(target):攔截 Object.isExtensible(proxy),返回一個布爾值。

11.setPrototypeOf(target, proto):攔截 Object.setPrototypeOf(proxy, proto),返回一個布爾值。如果目標對象是函數,那么還有兩種額外操作可以攔截。

12.apply(target, object, args):攔截 Proxy 實例作為函數調用的操作,比如 proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。

13.construct(target, args):攔截 Proxy 實例作為構造函數調用的操作,比如 new proxy(...args)。

如我們通過 delete 刪除其中一個元素時,可以通過deleteProperty()方法來攔截這個操作。還是上面代理 person 的代碼,我們添加一個 deleteProperty:

const person = { name: ’wenzi’, age: 20,};const personProxy = new Proxy(person, { // 忽略get和set方法,與上面一樣 // ... deleteProperty(target, key, receiver) { console.log(`delete key ${key}`); delete target[key]; },});

當執(zhí)行 delete 操作時:

delete personProxy[’age’];// log: delete key age2. Proxy 與 Reflect

Proxy 與 Reflect 可以說形影不離了,Reflect 里所有的方法和使用方式與 Proxy 完全一樣。

例如上面 Proxy 里的 get(), set()和 deleteProperty()方法我們都是直接操作原代理對象的,這里我們改成使用Reflect來操作:

const personProxy = new Proxy(person, { get(target, key, receiver) { console.log(`get value by ${key}`); return Reflect.get(target, key, receiver); }, set(target, key, value, receiver) { console.log(`set ${key}, old value ${target[key]} to ${value}`); return Reflect.set(target, key, value, receiver); }, deleteProperty(target, key, receiver) { console.log(`delete key ${key}`); return Reflect.deleteProperty(target, key, receiver); },});

可以發(fā)現完美地實現這些功能。

JS帶你深入領略Proxy的世界

3. 代理數組

我們在之前的文章 Vue 中對數組特殊的操作 中,討論過 Vue 為什么沒有使用Object.defineProperty來劫持數據,而是重寫了 Array 原型鏈上的幾個方法,通過這幾個方法來實現 Vue 模板中數據的更新。

但若 Proxy 的話,就可以直接代理數組:

const arr = [1, 2, 3, 4];const arrProxy = new Proxy(arr, { get(target, key, receiver) { console.log(’arrProxy.get’, target, key); return Reflect.get(target, key, receiver); }, set(target, key, value, receiver) { console.log(’arrProxy.set’, target, key, value); return Reflect.set(target, key, value, receiver); }, deleteProperty(target, key) { console.log(’arrProxy.deleteProperty’, target, key); return Reflect.deleteProperty(target, key); },});

現在我們再來操作一下代理后的數組 arrProxy 看下:

arrProxy[2] = 22; // arrProxy.set (4) [1, 2, 3, 4] 2 22arrProxy[3]; // arrProxy.get (4) [1, 2, 22, 4] 3delete arrProxy[2]; // arrProxy.deleteProperty (4) [1, 2, 22, 4] 2arrProxy.push(5); // push操作比較復雜,這里進行了多個get()和set()操作arrProxy.length; // arrProxy.get (5) [1, 2, empty, 4, 5] length

可以看到無論獲取、刪除還是修改數據,都可以感知到。還有數組原型鏈上的一些方法,如:

1.push()

2.pop()

3.shift()

4.unshift()

5.splice()

6.sort()

7.reverse()

也都能通過 Proxy 中的代理方法劫持到。

concat()方法比較特殊的是,他是一個賦值操作,并不改變原數組,因此在調用 concat()方法操作數組時,如果沒有賦值操作,那么這里只有 get()攔截到。

JS帶你深入領略Proxy的世界

4. 代理函數

Proxy 中還有一個apply()方法,是表示自己作為函數調用時,被攔截的操作。

const getSum = (...args) => { if (!args.every((item) => typeof item === ’number’)) { throw new TypeError(’參數應當均為number類型’); } return args.reduce((sum, item) => sum + item, 0);};const fnProxy = new Proxy(getSum, { /** * @params {Fuction} target 代理的對象 * @params {any} ctx 執(zhí)行的上下文 * @params {any} args 參數 */ apply(target, ctx, args) { console.log(’ctx’, ctx); console.log(`execute fn ${getSum.name}, args: ${args}`); return Reflect.apply(target, ctx, args); },});

執(zhí)行 fnProxy:

// 10, ctx為undefined, log: execute fn getSum, args: 1,2,3,4fnProxy(1, 2, 3, 4);// ctx為undefined, Uncaught TypeError: 參數應當均為number類型fnProxy(1, 2, 3, ’4’);// 10, ctx為window, log: execute fn getSum, args: 1,2,3,4fnProxy.apply(window, [1, 2, 3, 4]);// 6, ctx為window, log: execute fn getSum, args: 1,2,3fnProxy.call(window, 1, 2, 3);// 6, ctx為person, log: execute fn getSum, args: 1,2,3fnProxy.apply(person, [1, 2, 3]);5. 一些簡單的應用場景

我們知道 Vue3 里已經用 Proxy 重寫了響應式系統,mobx 也已經用了 Proxy 模式。在可見的未來,會有更多的 Proxy 的應用場景,我們這里也稍微講解幾個。

5.1 統計函數被調用的上下文和次數

這里我們用 Proxy 來代理函數,然后函數被調用的上下文和次數。

const countExecute = (fn) => { let count = 0; return new Proxy(fn, { apply(target, ctx, args) { ++count; console.log(’ctx上下文:’, ctx); console.log(`${fn.name} 已被調用 ${count} 次`); return Reflect.apply(target, ctx, args); }, });};

現在我們來代理下剛才的getSum()方法:

const getSum = (...args) => { if (!args.every((item) => typeof item === ’number’)) { throw new TypeError(’參數應當均為number類型’); } return args.reduce((sum, item) => sum + item, 0);};const useSum = countExecute(getSum);useSum(1, 2, 3); // getSum 已被調用 1 次useSum.apply(window, [2, 3, 4]); // getSum 已被調用 2 次useSum.call(person, 3, 4, 5); // getSum 已被調用 3 次5.2 實現一個防抖功能

基于上面統計函數調用次數的功能,也給我們實現一個函數的防抖功能添加了靈感。

const throttleByProxy = (fn, rate) => { let lastTime = 0; return new Proxy(fn, { apply(target, ctx, args) { const now = Date.now(); if (now - lastTime > rate) {lastTime = now;return Reflect.apply(target, ctx, args); } }, });};const logTimeStamp = () => console.log(Date.now());window.addEventListener(’scroll’, throttleByProxy(logTimeStamp, 300));

logTimeStamp()至少需要 300ms 才能執(zhí)行一次。

5.3 實現觀察者模式

我們在這里實現一個最簡單類 mobx 觀察者模式。

const list = new Set();const observe = (fn) => list.add(fn);const observable = (obj) => { return new Proxy(obj, { set(target, key, value, receiver) { const result = Reflect.set(target, key, value, receiver); list.forEach((observer) => observer()); return result; }, });};const person = observable({ name: ’wenzi’, age: 20 });const App = () => { console.log(`App -> name: ${person.name}, age: ${person.age}`);};observe(App);

person就是使用 Proxy 創(chuàng)建出來的代理對象,每當 person 中的屬性發(fā)生變化時,就會執(zhí)行 App()函數。這樣就實現了一個簡單的響應式狀態(tài)管理。

6. Proxy 與 Object.defineProperty 的對比

上面很多例子用Object.defineProperty也都是可以實現的。那么這兩者都各有什么優(yōu)缺點呢?

6.1 Object.defineProperty 的優(yōu)劣

Object.defineProperty的兼容性可以說比 Proxy 要好很多,出特別低的 IE6,IE7 瀏覽器外,其他瀏覽器都有支持。

但 Object.defineProperty 支持的方法很多,并且主要是基于屬性進行攔截的。因此在 Vue2 中只能重寫 Array 原型鏈上的方法,來操作數組。

6.2 Proxy 的優(yōu)劣

Proxy與上面的正好相反,Proxy 是基于對象來進行代理的,因此可代理更多的類型,例如 Object, Array, Function 等;而且代理的方法也多了很多。

劣勢就是兼容性不太好,即使用 polyfill,也無法完美的實現。

7. 總結

Proxy 能實現的功能還有很多,后面我們也會繼續(xù)進行探索,并且盡可能去了解下基于 Proxy 實現的類庫,例如 mobx5 的源碼和實現原理等。

以上就是JS帶你深入領略Proxy的世界的詳細內容,更多關于JS中的代理Proxy的資料請關注好吧啦網其它相關文章!

標簽: JavaScript
相關文章:
主站蜘蛛池模板: 2021国产精品自在拍在线播放 | 免费在线国产视频 | 国产视频高清在线 | 午夜美女影院 | 久久免费国产视频 | 香港毛片免费观看 | 97视频免费公开成人福利 | 999久久久精品视频在线观看 | 国产一区二区精品久久91 | 国产精品1区2区 | 亚洲成a人不卡在线观看 | 在线观看片成人免费视频 | 国产欧美一区二区久久 | 99视频在线免费看 | 亚洲精品中文字幕在线 | 亚洲欧美另类色妞网站 | 又刺激又黄的一级毛片 | 手机毛片在线 | 全免费a级毛片免费毛视频 全午夜免费一级毛片 | 亚洲精品国产综合久久一线 | 色综合天天综合网看在线影院 | 欧美一级高清片欧美国产欧美 | 成人做爰www | 国产视频精品久久 | 国产伦精品一区三区视频 | 国产成人久久精品推最新 | 国产三级日本三级美三级 | 国产免费人视频在线观看免费 | 欧美在线日韩在线 | 国产精品午夜性视频 | 亚洲天堂视频在线观看 | 国产一级毛片免 | 久久精品毛片 | 暖暖视频日韩欧美在线观看 | 亚洲成人精品久久 | 国产精品密蕾丝视频 | 国产精品国产国产aⅴ | 亚洲第一大网站 | 欧美高清一级毛片免费视 | 精品欧美日韩一区二区 | 国产亚洲精品久久久久91网站 |