关于javascript:深度解析Proxy使用Proxy和defineProperty的区别

8次阅读

共计 3054 个字符,预计需要花费 8 分钟才能阅读完成。

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

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

正文完
 0