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 safarineedProxyObj.child = 'sun'; // sun , 没有被拦挡console.log(proxyObj.child); // sunneedProxyObj === 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的区别,至于其余更粗疏的用发 能够参考 大佬的 文章