摘要:我們先以框架出發(fā),探索其中數(shù)據(jù)劫持的奧秘。針對對象在數(shù)據(jù)劫持這個(gè)問題上,可以被認(rèn)為是的升級版。技術(shù)支持監(jiān)測數(shù)組的等方法操作,支持對象屬性的動(dòng)態(tài)添加和刪除,極大的簡化了響應(yīng)化的代碼量。
隨著前端界的空前繁榮,各種框架橫空出世,包括各類mvvm框架百家爭鳴,比如Anglar、Vue、React等等,它們最大的優(yōu)點(diǎn)就是可以實(shí)現(xiàn)數(shù)據(jù)綁定,再也不需要手動(dòng)進(jìn)行DOM操作了,它們實(shí)現(xiàn)的原理也基本上是臟檢查或數(shù)據(jù)劫持。我們先以Vue框架出發(fā),探索其中數(shù)據(jù)劫持的奧秘。
Vue 2.0的版本所使用的數(shù)據(jù)劫持,說白了就是通過Object.defineProperty()來劫持對象屬性的setter和getter操作,在數(shù)據(jù)變動(dòng)時(shí)做你想要做的事情,舉個(gè)栗子:
var data = { name:"xiaoming" } Object.keys(data).forEach(function(key){ Object.defineProperty(data,key,{ get:function(){ console.log("get"); }, set:function(){ console.log("監(jiān)聽到數(shù)據(jù)發(fā)生了變化"); } }) }); data.name //控制臺會打印出 “get” data.name = "xiaohong" //控制臺會打印出 "監(jiān)聽到數(shù)據(jù)發(fā)生了變化"
但是有沒有比Object.defineProperty更好的實(shí)現(xiàn)方式呢?
答案是肯定的有,那就是我們今天的主人公:Proxy
1、Proxy簡介
Proxy這個(gè)詞的原意是代理,用在這里表示由它來代理某些操作,可以譯為代理器。
也可以理解成在目標(biāo)對象之前設(shè)置一層攔截,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機(jī)制,可以對外界的訪問進(jìn)行過濾和改寫。
在生活中,代理模式的場景是十分常見的,例如我們現(xiàn)在如果有購買海外產(chǎn)品(給女朋友買一個(gè)LV的包包,前提是你要先有個(gè)女朋友,^_^)的需求,更多的是去找代購中介機(jī)構(gòu),而不是直接去國外買。此時(shí),代購起到的作用就是代理的作用。
Proxy構(gòu)造函數(shù)能夠讓我們輕松的使用代理模式:
var proxy = new Proxy(target, handler);
Proxy構(gòu)造函數(shù)中有兩個(gè)參數(shù):
target是用Proxy包裝的被代理對象(可以是任何類型的對象,包括原生數(shù)組,函數(shù),甚至另一個(gè)代理)。
handler是一個(gè)對象,其聲明了代理target 的一些操作,其屬性是當(dāng)執(zhí)行一個(gè)操作時(shí)定義代理的行為的函數(shù)。
講的通俗點(diǎn),如何讓代購幫你買LV的包包呢?
首先,你需要告訴代購你看好了哪款包包,這個(gè)款式就是Proxy里的第一個(gè)參數(shù)target。
其次就是制定購買策略,例如國外比國內(nèi)便宜20%,就買2個(gè),便宜40%,就買4個(gè),這個(gè)策略就是第二個(gè)參數(shù)handle。
2、Proxy中的處理方法
Proxy有13種數(shù)據(jù)劫持的操作,那是相當(dāng)?shù)膹?qiáng)大:
2.1 get方法
get方法是在你得到某對象屬性值時(shí)預(yù)處理的方法,接受兩個(gè)常用參數(shù)
target:得到的目標(biāo)值
key:目標(biāo)的key值,相當(dāng)于對象的屬性
可以代購來模擬handle中的get方法,如下
var Bao = { name: "LV", price:9999, }; var proxyBao = new Proxy(Bao, { get: function(target, key) { if (target["price"]>5000) { return "超出客戶心理價(jià)位,不買了"; } else { return "符合客戶心理預(yù)期,買買買"; } } }); proxyBao.price //"超出客戶心理價(jià)位,不買了"
解釋一下:客戶想買一個(gè)LV的包,心理價(jià)位是5000,把購買目標(biāo)和需求都告訴了代購,代購詢問了下國外的價(jià)格,這款LV的包是9999,超出了客戶的心理價(jià)位,于是不買了。
2.2 set方法
set方法用來攔截某個(gè)屬性的賦值操作,可以接受四個(gè)參數(shù)
target:目標(biāo)值。
key:目標(biāo)的Key值。
value:要改變的值。
receiver:改變前的原始值。
假定Person對象有一個(gè)age屬性,該屬性應(yīng)該是一個(gè)不大于 200 的整數(shù),那么可以使用Proxy保證age的屬性值符合要求。
let validator = { set: function(target, key, value) { if (key === "age") { if (!Number.isInteger(value)) { throw new TypeError("The age is not an integer"); } if (value > 200) { throw new RangeError("The age seems invalid"); } } // 對于滿足條件的 age 屬性以及其他屬性,直接保存 target[key] = value; } }; let person = new Proxy({}, validator); person.age = 100; person.age // 100 person.age = "young" // 報(bào)錯(cuò) The age is not an integer person.age = 300 // 報(bào)錯(cuò) The age seems invalid
上面代碼中,由于設(shè)置了存值函數(shù)set,任何不符合要求的age屬性賦值,都會拋出一個(gè)錯(cuò)誤,這是數(shù)據(jù)驗(yàn)證的一種實(shí)現(xiàn)方法。
3、Proxy相比Object.defineProperty的優(yōu)勢
3.1 支持?jǐn)?shù)組
let arr = [1,2,3] let proxy = new Proxy(arr, { get (target, key, receiver) { console.log("get", key) return Reflect.get(target, key, receiver) }, set (target, key, value, receiver) { console.log("set", key, value) return Reflect.set(target, key, value, receiver) } }) proxy.push(4) // 能夠打印出很多內(nèi)容 // get push (尋找 proxy.push 方法) // get length (獲取當(dāng)前的 length) // set 3 4 (設(shè)置 proxy[3] = 4) // set length 4 (設(shè)置 proxy.length = 4)
Proxy 不需要對數(shù)組的方法進(jìn)行重載,省去了眾多 hack,減少代碼量等于減少了維護(hù)成本,而且標(biāo)準(zhǔn)的就是最好的。
3.2 針對對象
在數(shù)據(jù)劫持這個(gè)問題上,Proxy 可以被認(rèn)為是 Object.defineProperty() 的升級版。外界對某個(gè)對象的訪問,都必須經(jīng)過這層攔截。因此它是針對 整個(gè)對象,而不是 對象的某個(gè)屬性,所以也就不需要對 keys 進(jìn)行遍歷。
let obj = { name: "Eason", age: 30 } let handler = { get (target, key, receiver) { console.log("get", key) return Reflect.get(target, key, receiver) }, set (target, key, value, receiver) { console.log("set", key, value) return Reflect.set(target, key, value, receiver) } } let proxy = new Proxy(obj, handler) proxy.name = "Zoe" // set name Zoe proxy.age = 18 // set age 18
3.3 嵌套支持
本質(zhì)上,Proxy 也是不支持嵌套的,這點(diǎn)和 Object.defineProperty() 是一樣的。因此也需要通過逐層遍歷來解決。Proxy 的寫法是在 get 里面遞歸調(diào)用 Proxy 并返回,代碼如下:
let obj = { info: { name: "eason", blogs: ["webpack", "babel", "cache"] } } let handler = { get (target, key, receiver) { console.log("get", key) // 遞歸創(chuàng)建并返回 if (typeof target[key] === "object" && target[key] !== null) { return new Proxy(target[key], handler) } return Reflect.get(target, key, receiver) }, set (target, key, value, receiver) { console.log("set", key, value) return Reflect.set(target, key, value, receiver) } } let proxy = new Proxy(obj, handler) // 以下兩句都能夠進(jìn)入 set proxy.info.name = "Zoe" proxy.info.blogs.push("proxy")
4、應(yīng)用實(shí)例
4.1 使用Proxy實(shí)現(xiàn)表單校驗(yàn)
let person = { name: "xiaoming", age: 30 } let handler = { set (target, key, value, receiver) { if (key === "name" && typeof value !== "string") { throw new Error("用戶姓名必須是字符串類型") } if (key === "age" && typeof value !== "number") { throw new Error("用戶年齡必須是數(shù)字類型") } return Reflect.set(target, key, value, receiver) } } let boy = new Proxy(person, handler) boy.name = "xiaohong" // OK boy.age = "18" // 報(bào)錯(cuò) 用戶年齡必須是數(shù)字類型
5、總結(jié)
Proxy本質(zhì)上屬于元編程非破壞性數(shù)據(jù)劫持,在原對象的基礎(chǔ)上進(jìn)行了功能的衍生而又不影響原對象,符合松耦合高內(nèi)聚的設(shè)計(jì)理念。
通俗的說Proxy在數(shù)據(jù)外層套了個(gè)殼,然后通過這層殼訪問內(nèi)部的數(shù)據(jù),就像下面的圖:
Proxy讓JS開發(fā)者很方便的使用代理模式,使函數(shù)更加強(qiáng)大,業(yè)務(wù)邏輯更加清楚。
Proxy 不但可以取代 Object.defineProperty 并且還擴(kuò)增了非常多的功能。Proxy 技術(shù)支持監(jiān)測數(shù)組的 push 等方法操作,支持對象屬性的動(dòng)態(tài)添加和刪除,極大的簡化了響應(yīng)化的代碼量。Vue 3.0的也會使用Proxy去實(shí)現(xiàn)部分核心代碼。
在業(yè)務(wù)開發(fā)時(shí)應(yīng)該注意Proxy使用場景,當(dāng)對象的功能變得復(fù)雜或者我們需要進(jìn)行一定的訪問限制時(shí),便可以考慮使用代理。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/109557.html
摘要:用于修改某些操作的默認(rèn)行為和訪問器屬性的行為類似在對象的前面多一層代理,對象字面量中定義屬性的特性方法,訪問器屬性此時(shí)屬性被定義為訪問器屬性不一樣的寫法中是用代理的寫法第一個(gè)參數(shù)是對象,第二個(gè)是要操作的方法對象也有兩個(gè)屬性,一個(gè)是目標(biāo)對象, 1,Proxy用于修改某些操作的默認(rèn)行為和訪問器屬性的行為類似,在對象的前面多一層代理, const obj = { ...
摘要:而未來的互聯(lián)網(wǎng)網(wǎng)絡(luò)鏈路日趨復(fù)雜,加重了安全事件發(fā)生。蘋果強(qiáng)制開啟標(biāo)準(zhǔn)蘋果宣布年月日起,所有提交到的必須強(qiáng)制開啟安全標(biāo)準(zhǔn),所有連接必須使用加密。最后是安全意識。 互聯(lián)網(wǎng)發(fā)展20多年,大家都習(xí)慣了在瀏覽器地址里輸入HTTP格式的網(wǎng)址。但前兩年,HTTPS逐漸取代HTTP,成為傳輸協(xié)議界的新寵。?早在2014年,由網(wǎng)際網(wǎng)路安全研究組織Internet Security Research Gr...
摘要:第四行是為了保存當(dāng)前語境下的,事實(shí)上在瀏覽器的調(diào)試工具中直接運(yùn)行這些代碼的時(shí)候,這個(gè)指向的就是全局對象,所以去掉這一行,將下面的改成,程序的運(yùn)行結(jié)果是一模一樣的。 在騰訊的AlloyTeam的Blog上發(fā)現(xiàn)了這樣的一款工具:AlloyLever(原blog地址:http://www.alloyteam.com/2016...),覺得非常有趣且實(shí)用。尤其是其實(shí)現(xiàn)的原理也并不復(fù)雜,卻可以給...
摘要:安全問題的分類按照所發(fā)生的區(qū)域分類后端安全問題所有發(fā)生在后端服務(wù)器應(yīng)用服務(wù)當(dāng)中的安全問題前端安全問題所有發(fā)生在瀏覽器單頁面應(yīng)用頁面當(dāng)中的安全問題按照團(tuán)隊(duì)中哪個(gè)角色最適合來修復(fù)安全問題分類后端安全問題針對這個(gè)安全問題,后端最適合來修復(fù)前端安全 安全問題的分類 按照所發(fā)生的區(qū)域分類 后端安全問題:所有發(fā)生在后端服務(wù)器、應(yīng)用、服務(wù)當(dāng)中的安全問題 前端安全問題:所有發(fā)生在瀏覽器、單頁面應(yīng)用、...
閱讀 3257·2021-11-15 11:37
閱讀 2466·2021-09-29 09:48
閱讀 3833·2021-09-22 15:55
閱讀 3033·2021-09-22 10:02
閱讀 2655·2021-08-25 09:40
閱讀 3249·2021-08-03 14:03
閱讀 1712·2019-08-29 13:11
閱讀 1583·2019-08-29 12:49