js代理(Proxy)的做用是對基礎操做(取值,賦值,調用函數等)進行自定義。e.g. 你有一個對象obj = {}
,當對其屬性賦值時obj.age = 5
,你但願作一些驗證好比age不能賦值爲string。這時代理就能夠幫上忙。javascript
const p = new Proxy(target, handler);複製代碼
target:能夠是任意對象,包括數組,函數,或者另外一個代理html
handler:屬性是預約義函數的對象,它定義了這個代理的行爲。這個對象內的函數叫作陷阱(traps),由於你一旦對代理進行操做,相關的陷阱就會被觸發。java
來看一個最基本的例子,如上賦值obj.age = 5數組
const obj = {}; const proxy = new Proxy(obj, { set(target, key, value, receiver) { if (key === 'age') { if (!Number.isInteger(value)) { return false; // 或者 throw new TypeError('only allow int'); } target[key] = value; return true; } } }); proxy.age = 5; // OK proxy.age = 'lol'; // 隱式賦值失敗,或拋錯複製代碼
set方法必須返回布爾值,表示賦值成功與否。返回false在嚴格模式下會拋錯TypeErrorbash
set函數的四個參數:markdown
target:你代理的真正對象,上面就是objapp
key: 你訪問的代理屬性,如上age函數
value:給上面屬性賦的值,如上5ui
receiver:被調用的對象,要麼是代理,要麼是繼承代理的對象。通常狀況下,如上,顯而易見就是proxy,可是某些狀況下代理並非最開始被調用的對象。e.g. 假設你賦值p.age = 5,p不是代理,p本身也沒有age屬性,可是p的原型鏈上有個代理,因此當那個代理的set被調用時,receiver實際上是p。this
ps:賦值和返回true的的那句有些人喜歡寫成
return Reflect.set(target, key, value, receiver);複製代碼
上面這個基本等價於target[key] = value; 可是它返回設置成功與否,因此省了一步哈。
上面說了set,基本上全部對對象進行基本操做的方法都有相應的trap。這裏只談一下has(), get(), apply(), construct()
const obj = {_secret: 'hidden property', show: 'show'}; const proxy = new Proxy(obj, { has(target, key) { if (key[0] === '_') { return false; } return key in target; } }); '_secret' in obj // false 'show' in obj // true複製代碼
這個trap是給in用的(不是for in),Reflect.has()也會觸發,上面例子基本上自我解釋了,很容易懂。必須返回boolean。
var p = new Proxy(target, { get: function(target, property, receiver) { } });複製代碼
p.foo或p[bar]都會觸發。能夠返回任意值。
var p = new Proxy(target, { apply: function(target, thisArg, argumentsList) { } });複製代碼
p(),p.call(this), p.apply(this)均可觸發,能夠返回任意值。
var p = new Proxy(target, { construct: function(target, argumentsList, newTarget) { } });複製代碼
new一個實例的時候觸發,必須返回一個對象。
newTarget:被調用的構造器,如上p
實現一個簡單的雙向綁定
<body> <input type="text" id="model"> <p id="word"></p> </body> <script> const model = document.getElementById("model") const word = document.getElementById("word") var obj= {}; const newObj = new Proxy(obj, { set: function(target, key, value, receiver) { if (key === "text") { model.value = value; word.innerHTML = value; } return Reflect.set(target, key, value, receiver); } }); // change ui will change local object model.addEventListener("keyup",function(e){ newObj.text = e.target.value }) // later on, if you want to programmaticly change local object, it will also reflect on UI newObj.text = 'something i like'; </script>複製代碼
取得輸入框和顯示框元素,給輸入框加監聽,若是用戶輸入了,那麼對代理的text屬性賦值, 這是ui改js,若是後面用戶想直接修改newObject,其改動也會反映在UI上,由於代理內部有給元素賦值。