es6 的 Proxy 用法

一、Proxy 介绍

Proxy:用于创建一个对象的代理,从而实现基本操作的拦截和自定义

二、Proxy 使用

Proxy 为构造函数用于生成 Proxy 实例

const proxy = new Proxy(target, handler)

// target 表示所要拦截的目标对象(任何类型的对象:原生数组,函数,甚至另一个代理)

// handler 通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时的代理行为

参数 handler 拦截属性

  • get(target,propKey,receiver):拦截对象属性的读取
  • set(target,propKey,value,receiver):拦截对象属性的设置
  • has(target,propKey):拦截propKey in proxy的操作,返回一个布尔值
  • deleteProperty(target,propKey):拦截delete proxy[propKey]的操作,返回一个布尔值
  • ownKeys(target):拦截Object.keys(proxy)for...in等循环,返回一个数组
  • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象
  • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc),返回一个布尔值
  • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值
  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象
  • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值
  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作

Reflect 介绍 

ES6 中操作对象而提供的新 API,若需要在 Proxy 内部调用对象的默认行为,建议使用 Reflect

基本特点:

  • 只要 Proxy 对象具有的代理方法,Reflect 对象全部具有,以静态方法的形式存在
  • 修改某些 Object 方法的返回结果,定义不存在属性行为的时候不报错返回false
  • 让 Object 操作都变成函数行为

get(target, propKey, receiver)接受三个参数,为目标对象,属性名,proxy 实例本身

// get 对数组增删改查进行拦截,数组读取负数的索引

const createArray = (...element) => {
    const handler = {
        get(target, propKey, receiver) {
            const index = Number(propKey);
            if (index < 0) {
                propKey = target.length + index + '';
            }
            return Reflect.get(target, propKey, receiver)
        }
    }
    let target = [];
    target.push(...element);
    return new Proxy(target, handler)
}

const arr = createArray('z', 'd', 'x');
const lastEle = arr[-1];
console.log(lastEle);

set(target, propKey, value, receiver)用来拦截某个属性的赋值操作,接受四个参数,为目标对象,属性名,属性值,proxy 实例本身

// 定义一个对象,规定年龄输入整数时才被赋值,访问无效属性时控制台提醒

const person = {
    name: 'zdx',
    age: 18,
}

const proxy = new Proxy(person, {
    get(target, propKey, receiver) {
        if (propKey in target) return Reflect.get(target, propKey, receiver);
        else {
            console.warn('对象没有此属性');
            return false;
        }
    },
    set(target, propKey, value, receiver) {
        if (propKey === 'age') {
            if (typeof value === 'number' && /(^[1-9]\d*$)/.test(value)) {
                return Reflect.set(target, propKey, value, receiver);
            } else {
                console.warn('年龄只能输入正整数');
                return fasle;
            }
        } else {
            return Reflect.set(target, propKey, value, receiver)
        }
    }
})

proxy.age = 20;
console.log(proxy.age); // 20
proxy.age = 21.001;
console.log(proxy.age); // 20
console.log(proxy.zdx); // false

deleteProperty(target, propKey)用于拦截 delete 操作,如果这个方法抛出错误或者返回 false,当前属性无法被 delete 命令删除

const handler = {
    deleteProperty(target, propKey) {
        if (propKey[0] === '_') {
            throw new Error('无法删除私有属性');
        }
        Reflect.deleteProperty(target, propKey);
        return true;
    }
};
const target = {
    _prop: 'test',
};
const proxy = new Proxy(target, handler);
delete proxy._prop; // Error:无法删除私有属性

取消代理

Proxy.revocable(target, handler);

 测试案例

const user = {
    _name: 'Guest',
    get name() {
        return this._name;
    }
}

const userProxy = new Proxy(user, {
    get(target, propKey, receiver) {
        return Reflect.get(target, propKey, receiver);
    }
});

const admin = {
    __proto__: userPoxy,
    _name: 'Admin',
};

console.log(admin.name); // Guest

// admin 自身不具备 name 属性,顺着原型链往上找 name,
// user 对 name 的访问返回当前对象的 _name 属性,
// userProxy 代理的是 user 对象,所以 this 指向 user 对象,而不是 admin 对象

 三、使用场景

Proxy 功能类似于设计模式中的代理模式,功能如下:

  • 拦截和监视外部对对象的访问
  • 降低函数或类的复杂度
  • 在复杂操作前对操作进行校验或对所需资源进行管理
    // 声明一个私有的 _apiKey,便于 api 这个对象内部的方法调用,但不希望对外部也访问 api._apiKey
    
    let api = {
        _apiKey: 'zdxtesttest',
    };
    
    const arr = ['_apiKey'];
    
    api = new Proxy(api, {
        get(target, propKey, receiver) {
            if (arr.indexOf(propKey) > -1) {
                throw new Error(`${propKey} 不可访问`);
            }
            return Reflect.get(target, propKey, receiver);
        },
        set(target, propKey, value, receiver) {
            if (arr.indexOf(propKey) > -1) {
                throw new Error(`${propKey} 不可修改`);
            }
            return Reflect.set(target, propKey, value, receiver);
        }
    })
    
    console.log(api._apiKey);
    api._apiKey = '123123';
    // 通过 proxy 实现观察者模式
    // 观察者模式:函数自动观察数据对象,一旦对象有变化,函数自动执行
    // observable 函数返回一个原始对象的 proxy 代理,拦截赋值操作,触发充当观察者的各个函数
    
    const queueObservers = new Set();
    
    const observe = fn => queueObservers.add(fn);
    
    const observable = obj => new Proxy(obj, {
        set(target, propKey, value, receiver) {
            queueObservers.forEach(observer => observer());
            return Reflect.set(target, propKey, value, receiver);
        }
    })
    
    // 观察者函数都放进 Set 集合中,当修改 obj 的值时,会在set函数中拦截,自动执行 Set 集合中的观察者

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值