Proxy 对于失常的业务同学来说很少遇到。明天凑巧遇到了,也是被 Proxy教育
了一番。
什么是 Proxy
Proxy 是 ES6 推出的一个类,给对象架设一层拦截器,凡是要 拜访
或者 批改
这个对象上的值或者属性,都必须先通过这层拦截器, Proxy 也叫 代理器
, 它代理了对对象的操作。
有人不经要问了,这个和 Object.definePrototype 有什么区别
什么是 Object.defineProperty
Object.definePrototype 是对对象上的属性进行新增或者批改, 有 2 种写法,数据描述符
或者 拜访器描述符
, IE8 不反对( 敲黑板, 面试官再问起来为什么 Vue 不反对 IE8, 就这么通知他
)
const obj = {name: 'chrome'}
// 数据描述符
Object.defineProperty(obj, 'age', {
configurable: true, // 这个定义是否能够被 delete
enumerable: true, // 这个值是否能够被 for in 枚举, 或者 Object.keys 获取到
writable: true, // 定义是否能够被批改
value: '100'
})
// 拜访器描述符
Object.defineProperty(obj, 'child', {
configurable: true,
enumerable: true,
set(value) {console.log(value)
},
get() {console.log(this.value)
}
})
Object.defineProperty 和 Proxy 的区别
- Object.defineProperty 对对象本身做批改, 而 Proxy 只是在 Object 根底上加一层拦挡,不批改原对象(其实并不是这样,对于不反对嵌套对象,如果你想监听嵌套的,那么这个中央就不对了。前面会说到)
- 监听不了数组的变动
- 监听伎俩比拟繁多,只能监听 set 和 get, Proxy 有 10 几种监听
- 必须得把所有的属性全副增加 defineProperty, Proxy 对整个对象都会进行拦挡
1. 为什么 Proxy 不必遍历每一个属性
var needProxyObj = {name: 'chrome', age:'800'}
var proxyObj = new Proxy(needProxyObj, {set(target, key, value, receiver) {consnole.log('proxy 批改了', key, value)
}
})
proxyObj.name = 'safari'; // proxy 批改了 name safari
Proxy 是代理在 对象
级别的,defineProperty 是代理到 动态的值
级别,所以 Proxy 的弱小就在这里
2. 为什么 Proxy 不批改原对象,为什么 Proxy 是在对象下面加一层代理?
var needProxyObj = {name: 'chrome', age:'800'}
var proxyObj = new Proxy(needProxyObj, {set(target, key, value, receiver) {consnole.log('proxy 批改了', key, value)
}
})
proxyObj.name = 'safari'; // proxy 批改了 name safari
needProxyObj.child = 'sun'; // sun , 没有被拦挡
console.log(proxyObj.child); // sun
needProxyObj === proxyObj; // false
看到没, 当我批改被代理之前的对象的时候,拦截器没有起作用,并且被代理的新对象 proxyObj
的child
值也跟着变动了, 然而 needProxyObj === proxyObj; // false
, 这又是 蜜汁操作
之一了。其实也好了解,代理对象和被代理的对象,他们在表面上是不一样的,其实在底层是同一个对象。
3. 为什么我又说 Proxy 不批改原对象也是不精确的。
这就波及到 Proxy 和 defineProperty 的一个独特个性,不反对对象嵌套。须要递归去实现。
var person = {
name: '阿巴',
age: '100',
child: {
name: '阿巴的儿子',
age: '88'
}
}
var proxyEvent = { }
var deepProxy = function(obj) {if (typeof obj === 'object') {Object.entries(obj).forEach(([key, value]) => {obj[key] = deepProxy(value);
})
return new Proxy(obj, proxyEvent)
}
return obj;
}
deepProxy(person);
能够复制以上代码看看
十分可怜的是,这时候原对象的 child 不在是一个单纯的孩子, 它被 Proxy 了
这就是我说为什么不精确的起因了, 所以万不得已,真心不举荐用递归的形式去设置 Proxy, 当然,有方法递归设置 Proxy,咱们就有方法给它还原
function proxyToObject(proxyObj) {const next = function (obj, mergeObj) {if (typeof obj === 'object') {Object.entries(obj).forEach(([key, value]) => {if (!value) {mergeObj[key] = value;
} else if (value instanceof Array) {mergeObj[key] = next(value, []);
} else if (value instanceof Object) {mergeObj[key] = next(value, {});
} else {mergeObj[key] = value;
}
})
}
if (obj && obj instanceof Array) {for (let value of obj) {mergeObj.push(next(value, {}));
}
}
return mergeObj;
}
return next(proxyObj, {});
}
proxyToObject(person); // 而后就复原了
为什么说 Proxy 能够监听数组的拜访
咱们都晓得在 Vue 中,因为 defineProperty 的局限性,数组的 push 被认为是 变异
的个性,为什么 vue 的 push 能够被监听到呢,是因为 vue 把数组对象的 push 给重写了,进行拦挡,这导致减少了不少额定的操作
来看看 Proxy 怎么实现
var arr = [1,2,3,4];
let arrProxy = new Proxy(arr, {get(target, propKey) {if (Array.isArray(target) && typeof Array.prototype[propKey] === 'function') {Promise.resolve().then(e => {console.log('操作了数组', propKey);
})
}
return target[propKey]
}
})
arrProxy.push(5);
console.log('push 完结了');
// push 完结了
// 操作了数组 push
为什么要应用 Promise.resolve()
, 是因为 push 是一个同步操作,在拜访 push 的时候还没有执行这个函数,所以想在 push 之后做一些事件,就能够用这个 微工作
机制来把操作滞后
以上通过代码的形式解释了一下 Proxy 和 definePrototype 的区别,至于其余更粗疏的用发 能够参考 大佬的 文章