【JS】深度解析Proxy使用,Proxy和defineProperty的区别。
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的区别,至于其他更细致的用发 可以参考 大佬的 文章
以上是 【JS】深度解析Proxy使用,Proxy和defineProperty的区别。 的全部内容, 来源链接: utcz.com/a/91311.html