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