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函数的四个参数:app
target:你代理的真正对象,上面就是obj函数
key: 你访问的代理属性,如上ageui
value:给上面属性赋的值,如上5this
receiver:被调用的对象,要么是代理,要么是继承代理的对象。通常状况下,如上,显而易见就是proxy,可是某些状况下代理并非最开始被调用的对象。e.g. 假设你赋值p.age = 5,p不是代理,p本身也没有age属性,可是p的原型链上有个代理,因此当那个代理的set被调用时,receiver实际上是p。spa
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上,由于代理内部有给元素赋值。