隨著前端界的空前繁榮,各種框架橫空出世,包括各類mvvm框架百家爭鳴,比如anglar、vue、react等等,它們最大的優點就是可以實現資料繫結,再也不需要手動進行dom操作了,它們實現的原理也基本上是髒檢查或資料劫持。我們先以vue框架出發,探索其中資料劫持的奧秘。
vue 2.0的版本所使用的資料劫持,說白了就是通過object.defineproperty()來劫持物件屬性的setter和getter操作,在資料變動時做你想要做的事情,舉個栗子:
var data =
object.keys(data).foreach(function(key),
set:function()
})});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方法是在你得到某物件屬性值時預處理的方法,接受兩個常用引數
可以**來模擬handle中的get方法,如下
var bao = ;
var proxybao = new proxy(bao, else
}});proxybao.price
//"超出客戶心理價位,不買了"
解釋一下:客戶想買乙個lv的包,心理價位是5000,把購買目標和需求都告訴了**,**詢問了下國外的**,這款lv的包是9999,超出了客戶的心理價位,於是不買了。
2.2 set方法
set方法用來攔截某個屬性的賦值操作,可以接受四個引數
假定person物件有乙個age屬性,該屬性應該是乙個不大於 200 的整數,那麼可以使用proxy保證age的屬性值符合要求。
let validator =
if (value > 200)
}// 對於滿足條件的 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, ,
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 =
let handler = ,
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 =
}let handler =
return reflect.get(target, key, receiver)
},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 =
let handler =
if (key === 'age' && typeof value !== 'number')
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使用場景,當物件的功能變得複雜或者我們需要進行一定的訪問限制時,便可以考慮使用**。
es6 Proxy 學習筆記
proxy 可以理解成,在目標物件之前架設一層 攔截 外界對物件的訪問都必須先通過這層攔截,通過這種 操作的機制,可以對外界的訪問進行過濾和修改。let person let obj new proxy person,obj.name lilei obj.age 暫無該值 proxy例項通過prox...
ES6 Proxy的學習與理解
前一段時間在位元組跳動時聊到了proxy。起因是問道vue中資料繫結的實現,回答通過設定setter和getter實現,問這樣有什麼缺點,答在對物件的屬性的監控方面存在瑕疵,例如通過直接設定陣列下標進行賦值,或者對物件直接進行修改,是無法觀察到的,必須使用vue.set新增,或者使用array.pr...
es6 proxy的簡單使用
proxy,顧名思義是 的意思 也就是對乙個操作的 比如當我們對乙個物件要進行操作的時候,我們是這樣寫的 展示出來的效果是這樣的 瀏覽器有點問題,所以在node的環境下執行 在使用了proxy後,我們的會變成這樣 結果如下 流程的大概是這樣,1.當我們用了上面的寫法後 不要問為什麼這麼寫,遵守是唯一...