【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

看到没, 当我修改被代理之前的对象的时候,拦截器没有起作用,并且被代理的新对象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了
【JS】深度解析Proxy使用,Proxy和defineProperty的区别。

这就是我说为什么不准确的原因了, 所以万不得已,真心不推荐用递归的方式去设置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

回到顶部