vue3中的數(shù)據(jù)劫持的最新實(shí)現(xiàn)方案的proxy示例詳解
vuejs中實(shí)現(xiàn)數(shù)據(jù)的劫持,v2中使用的是Object.defineProperty()來實(shí)現(xiàn)的,在大版本v3中徹底重寫了這部分,使用了proxy這個(gè)數(shù)據(jù)代理的方式,來修復(fù)了v2中對(duì)數(shù)組和對(duì)象的劫持的遺留問題。
proxy是什么
Proxy 用于修改某些操作的默認(rèn)行為,等同于在語言層面做出修改,所以屬于一種“元編程”(meta programming),即對(duì)編程語言進(jìn)行編程。
Proxy 可以理解成對(duì)源對(duì)象進(jìn)行一個(gè)封裝,在操作源對(duì)象之前,做了一系列額外的操作,最終返回我們需要的新數(shù)據(jù)對(duì)象。
基礎(chǔ)使用
let obj = new Proxy( {}, { get(target, prop, receiver) { console.log("get", prop); if (!target[prop]) target[prop] = 120; return Reflect.get(target, prop, receiver); }, set(target, prop, value, receiver) { console.log("set", prop); return Reflect.set(target, prop, value, receiver); }, } ); obj.count = 1; obj.count; obj.count; obj.count; console.log(obj.count); obj.age; console.log(obj.age);
proxy
實(shí)例有兩個(gè)參數(shù),一個(gè)是目標(biāo)對(duì)象,一個(gè)是操作方法的hash
集合
取值函數(shù)get
,賦值函數(shù)set
。
對(duì)特定屬性的劫持
const proxyObj = new Proxy( { name: "Tom", age: 18 }, { get: function (target, prop) { if (prop === "age") return target[prop] - 1; return 35; }, } ); proxyObj.time; console.log("?? ~ proxyObj.time:", proxyObj.time); proxyObj.age; console.log("?? ~ proxyObj.time:", proxyObj.age);
把實(shí)例方法封裝在對(duì)象內(nèi)部
const object = { name: "Tom", age: 18, sayHi() { console.log("sayHi"); }, proxy() { return new Proxy(this, { get(target, prop) { console.log("?? ~ get ~ prop:", prop); if (prop in target) { return Reflect.get(target, prop); } else { return "no prop"; } return Reflect.get(target, prop); }, }); }, }; const newProxy = object.proxy(); // newObjj.age; console.log("?? ~ newObjj.age;:", newProxy.age); console.log("?? ~ newObjj.name;:", newProxy.sex);
對(duì)數(shù)組進(jìn)行負(fù)值索引的操作
function createArray(...elements) { let handler = { get(target, prop, receiver) { let index = Number(prop); if (index < 0) { prop = String(target.length + index); } return Reflect.get(target, prop, receiver); }, }; let target = []; target.push(...elements); return new Proxy(target, handler); } let arr = createArray("a", "b", "c"); arr[-1]; console.log("?? ~ arr[-1]:", arr[-1]); console.log("?? ~ arr[-1]:", arr[-2]); console.log("?? ~ arr[-1]:", arr[-3]);
實(shí)現(xiàn)數(shù)據(jù)的鏈?zhǔn)秸{(diào)用
var double = (n) => n * 2; var pow = (n) => Math.pow(n, 2); var reverse = (n) => String(n).split("").reverse().join(""); const pipe = function (value) { var funcStack = []; var oProxy = new Proxy( {}, { get: function (target, key) { console.log("?? ~ pipe ~ key:", key); if (key === "get") { return funcStack.reduce(function (val, func) { return func(val); }, value); } // 把方法存儲(chǔ)到棧中 funcStack.push(window[key]); console.log("?? ~ funcStack:", funcStack); return oProxy; }, } ); return oProxy; }; const data1 = pipe(3).double.pow.reverse.get; console.log("?? ~ data:", data1);
注意:三個(gè)方法必須是var
聲明的,let/const
都會(huì)報(bào)錯(cuò)
上面代碼設(shè)置 Proxy 以后,達(dá)到了將函數(shù)名鏈?zhǔn)绞褂玫男Ч?/p>
利用get攔截,實(shí)現(xiàn)一個(gè)生成各種 DOM
節(jié)點(diǎn)的通用函數(shù)dom
const dom = new Proxy( {}, { get(target, prop) { return function (arrts, ...children) { const el = document.createElement(prop); for (let prop of Object.keys(arrts)) { el.setAttribute(prop, arrts[prop]); } for (let child of children) { console.log("?? ~ get ~ child:", child); if (typeof child === "string") { child = document.createTextNode(child); } el.appendChild(child); } return el; }; }, } ); const el = dom.div( {}, "Hello, my name is ", dom.a({ href: "http://example.com" }, "Mark"), ". I like:", dom.ul( {}, dom.li({}, "The web"), dom.li({}, "Food"), dom.li({}, "…actually that's it") ) ); document.body.appendChild(el);
第三個(gè)參數(shù),它總是指向原始的讀操作所在的那個(gè)對(duì)象
const proxy = new Proxy( {}, { get: function (target, prop, receiver) { console.log("?? ~ prop:", prop); return receiver; }, } ); const isSame = proxy.getReceiver === proxy; console.log("?? ~ isSame:", isSame); const d = Object.create(proxy); console.log("ddd", d.a === d);
如果一個(gè)屬性不可配置(configurable)且不可寫(writable),則 Proxy 不能修改該屬性
const target = Object.defineProperties( {}, { foo: { value: "bar", enumerable: false, configurable: false }, } ); const handler = { get(target, prop) { return "abc"; }, }; const proxy2 = new Proxy(target, handler); proxy2.foo;
上面通過 Proxy 對(duì)象訪問該屬性會(huì)報(bào)錯(cuò)。
攔截方法的執(zhí)行
上面的都是object對(duì)象的屬性進(jìn)行劫持,也可以作為方法調(diào)用時(shí)進(jìn)行劫持。
var target = function () { return "I am the target"; }; var handler = { apply(target, thisArg, argumentsList) { console.log("?? ~ apply ~ argumentsList:", argumentsList); const res = target(); console.log("?? ~ apply ~ res:", res); return "I am the proxy" + " " + argumentsList.join(","); }, }; const p = new Proxy(target, handler); const a = p("a", "b"); console.log("?? ~ a:", a);
變量p是 Proxy 的實(shí)例,當(dāng)它作為函數(shù)調(diào)用時(shí)(p()),就會(huì)被apply方法攔截,返回一個(gè)字符串
function sum(left, right) { return left + right; } var twice = { apply(target, context, args) { console.log("?? ~ apply ~ context:", context); console.log("?? ~ apply ~ args:", args); return Reflect.apply(target, context, args) * 2; }, }; const proxy = new Proxy(sum, twice); const data = proxy(1, 2); console.log("?? ~ data:", data); const data2 = proxy.call(null, 2, 5); console.log("?? ~ data2:", data2); const data3 = proxy.apply(null, [5, 5]); console.log("?? ~ data3:", data3);
當(dāng)執(zhí)行proxy函數(shù)(直接調(diào)用或call和apply調(diào)用),就會(huì)被apply方法攔截。
get和set方法,實(shí)現(xiàn)內(nèi)部屬性的保護(hù)機(jī)制
const proxy = new Proxy( {}, { get(target, prop) { invariant(prop, "get"); return Reflect.get(target, prop); }, set(target, prop, value) { invariant(prop, "set"); Reflect.set(target, prop, value); return true; }, } ); function invariant(key, action) { if (key[0] === "_") { throw new Error(`Invalid attempt to ${action} private "${key}" property`); } } // proxy._prop; proxy._prop = "value";
攔截key in proxy的操作
var target = { _prop: "foo", prop: "foo" }; const proxy = new Proxy(target, { has(target, key) { if (key[0] === "_") { console.log("false"); return false; } return key in target; }, }); "_prop" in proxy; // false
deleteProperty刪除屬性的劫持
const handler = { construct(target, args) { console.log("called: " + args.join(",")); return new target(...args); }, deleteProperty(target, prop) { if (prop === "age") return false; delete target[prop]; return true; }, }; const P = new Proxy(function () {}, handler); const p = new P(10); P.value; const p2 = new Proxy( { age: 20, name: "John", greet: () => console.log("hello"), }, handler ); delete p2.age; delete p2.name;
到此這篇關(guān)于vue3中的數(shù)據(jù)劫持的最新實(shí)現(xiàn)方案的proxy的詳解的文章就介紹到這了,更多相關(guān)vue proxy內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vuex處理用戶Token過期及優(yōu)化設(shè)置封裝本地存儲(chǔ)操作模塊
這篇文章主要為大家介紹了Vuex處理用戶Token優(yōu)化設(shè)置封裝本地存儲(chǔ)操作模塊及Token?過期問題詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09解決vue使用vant輪播組件swipe + flex時(shí)文字抖動(dòng)問題
這篇文章主要介紹了解決vue使用vant輪播組件swipe + flex時(shí)文字抖動(dòng)問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2021-01-01vue3?組件與API直接使用的方法詳解(無需import)
這篇文章主要介紹了vue3?組件與API直接使用的方法(無需import),主要包括vue3自動(dòng)導(dǎo)入和API的自動(dòng)引入問題,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09vue.js中toast用法及使用toast彈框的實(shí)例代碼
這篇文章主要介紹了vue.js中toast用法及使用toast彈框的實(shí)例代碼,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒加載,需要的朋友可以參考下2018-08-08VUE對(duì)Storage的過期時(shí)間設(shè)置,及增刪改查方式
這篇文章主要介紹了VUE對(duì)Storage的過期時(shí)間設(shè)置,及增刪改查方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02vue.js實(shí)現(xiàn)格式化時(shí)間并每秒更新顯示功能示例
這篇文章主要介紹了vue.js實(shí)現(xiàn)格式化時(shí)間并每秒更新顯示功能,結(jié)合實(shí)例形式分析了vue.js時(shí)間格式化顯示與基于定時(shí)器進(jìn)行實(shí)時(shí)更新的相關(guān)操作技巧,需要的朋友可以參考下2018-07-07