1、简介
数据劫持,指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。
经典的应用是Vue双向数据绑定。
常见的数据劫持实现方法有两种:
- Object.defineProperty
- Proxy设置代理(ES6新增)
本篇文章介绍Proxy的使用。
2、Proxy
2.1、介绍
new Proxy(target, handler)
- 参数:
- target:要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
- handler:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理的行为。
- handler中常⽤的对象⽅法:
- get(target, propKey, receiver):拦截对象的读取属性操作。
- set(target, propKey, value, receiver):设置属性值操作的捕获器。
- has(target, propKey):对in操作符的代理方法。
- construct(target, args):拦截new操作符,返回的是一个对象。
- apply(target, object, args):拦截函数的调用。
2.2、代码示例
2.2.1、get(target, propKey, receiver)
拦截对象的读取属性操作。
- target:目标对象。
- propKey:要获取的key值。
- receiver:Proxy代理自身。
const target = {
name: 'cxk'
};
const handler = {
get(target, propKey, receiver) {
console.log('---调用了get方法---');
console.log(target, propKey, receiver);
return target[propKey];
}
};
const proxy = new Proxy(target, handler);
console.log(target.name); // cxk
console.log(proxy.name);
// ---调用了get方法---
// {name: 'cxk'} 'name' Proxy {name: 'cxk'}
// cxk
2.2.2、set(target, propKey, value, receiver)
设置属性值操作的捕获器。
- target:目标对象。
- propKey:要添加或修改的key值。
- value:要把key设置成什么值。
- receiver:Proxy代理自身。
const target = {
name: 'cxk'
};
const handler = {
set(target, propKey, value, receiver) {
console.log('---调用了set方法---');
console.log(target, propKey, value, receiver);
target[propKey] = value;
}
};
const proxy = new Proxy(target, handler);
// 通过proxy添加或修改属性,触发set方法,并修改成功
proxy.age = 18;
console.log(proxy); // Proxy {name: 'cxk', age: 18}
console.log(target); // {name: 'cxk', age: 18}
// 通过target添加或修改属性,不触发set方法,也修改成功
target.age = 20;
console.log(proxy); // Proxy {name: 'cxk', age: 20}
console.log(target); // {name: 'cxk', age: 20}
2.2.3、has(target, propKey)
对in操作符的代理方法。
- target:目标对象。
- propKey:in操作符左边的参数。
const target = {
name: 'cxk',
_age: 18
};
const handler = {
has(target, propKey) {
console.log('---触发了has方法---');
console.log(target, propKey);
// 对象内部的私有属性以_开头,外部不可以访问。
if (propKey.includes('_')) {
return false;
}
return propKey in target;
}
};
const proxy = new Proxy(target, handler);
console.log('_age' in target); // true
console.log('_age' in proxy); // false
// ---触发了has方法---
// {name: 'cxk', _age: 18} '_age'
// false
2.2.4、construct(target, args)
拦截new操作符,返回的是一个对象。
- target:目标对象,也就是构造函数。
- args:被调用时的参数,是一个类数组。
function target(name) {
this.name = name;
}
const handler = {
construct(target, args) {
console.log('---触发了construct方法---');
console.log(target, args);
return new target(...args);
}
};
const proxy = new Proxy(target, handler);
new proxy('cxk', 18);
// ---触发了construct方法---
// ƒ target(name) {
// this.name = name;
// } (2) ['cxk', 18]
2.2.5、apply(target, object, args)
拦截函数的调用。
- target:目标对象,也就是函数。
- object:目标对象的上下文对象(this)。
- args:被调用时的参数,是一个类数组。
function sum(a, b) {
return a + b;
}
const handler = {
apply(target, object, args) {
console.log('---触发了apply方法---');
console.log(target, object, args);
return target(...args);
}
};
const proxy = new Proxy(sum, handler);
console.log(proxy(1, 2));
// ---触发了apply方法---
// ƒ sum(a, b) {
// return a + b;
// }
// undefined
// [1, 2]
// 3
let obj = {
a: 1,
b: 2
}
console.log(proxy.call(obj, 3, 3));
// ---触发了apply方法---
// ƒ sum(a, b) {
// return a + b;
// }
// {a: 1, b: 2}
// [3, 3]
// 6
- Proxy代理的handler对象方法很多,上述只列举了最常用的几个,其他不怎么常用的对象方法的需要读者自行搜索学习。