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

看到没, 当我批改被代理之前的对象的时候,拦截器没有起作用,并且被代理的新对象proxyObjchild值也跟着变动了, 然而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的区别,至于其余更粗疏的用发 能够参考 大佬的 文章