JS数据劫持 之 Proxy设置代理

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对象方法很多,上述只列举了最常用的几个,其他不怎么常用的对象方法的需要读者自行搜索学习。
posted @ 2022-11-29 09:31  笔下洛璃  阅读(631)  评论(0编辑  收藏  举报