《es6标准入门》知识整理(3) - Proxy

Proxy介绍

Proxy 用于修改某些操做的默认行为;能够理解成,在目标对象以前架设一层"拦截",外界对对象的访问,都必须先经过这层拦截,所以提供了一层机制,能够对外界的访问进行过滤和改写。javascript

const proxy = new Proxy(target,handler); target表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
    const obj = new Proxy({}, {
                get: function(target, key, receiver) {
                    console.log(`getting ${key}`);
                    return Reflect.get(target, key, receiver);
                },
                set: function(target, key, value,receiver) {
                    console.log(`setting ${key}`)
                    return Reflect.set(target, key, value, receiver)
                }
            })
     obj.count = 1
     console.log(++obj.count)//2

proxy实例的常见方法

get(target,prop,receiver ):拦截某个属性的读取,三个参数依次为目标对象、属性名和 proxy 实例自己 receiver, 其中 receiver 为可选;java

function createArray(...elements) {
                let handler = {
                    get(target, propKey, receiver) {
                        console.log(propKey)
                        let index = Number(propKey);
                        if(index < 0) {
                            propKey = String(target.length + index);
                        }
                        return Reflect.get(target, propKey, receiver)
                    }
                };
                let target = [];
                target.push(...elements);
                return  new Proxy(target, handler);
     }
    let arr = createArray('a','b','c');
    console.log(arr[-1]);// c
    console.log(arr[0]);// b
    console.log(arr[1])// a

这个例子用 get 拦截,实现根据数组索引读取数组;当索引为负时,表示数组遍历为从后到前;因此,当数组的位置是 -1 的时候,就会输出数组的倒数第一个成员 c数组

set(target,prop, value, receiver):用来拦截某个属性的赋值操做; prop 为属性名,value 为属性值,receiver 依旧可选;app

用set()做为数据验证:函数

let validator= {
                set: function(obj,prop,value) {
                    if(prop ==='age') {
                        if(!Number.isInteger(value)) {
                            throw new TypeError('The age is not an integer')
                        }
                        if(value > 200) {
                            throw new RangeError('The age seems invalid')
                        }
                    }
                    // 对于age之外的属性,直接保存
                    obj[prop] = value;
                }
    }
    let person = new Proxy({}, validator)
    person.age = 100;
    console.log(person.age)// 100
    person.age = 300;
    console.log(person.age);// 报错

任何不符合要求的age属性赋值,都会报错。this

apply(target,ctx, args):拦截函数的调用,call 和 apply 操做;参数 ctx 指的是目标对象的上下文对象 (this) 和目标对象的参数数组。代理

const twice = {
          apply(target, ctx, args) {
          console.log(args);// 1,2
          return Reflect.apply(...arguments)*2;
       }
    };
    function sum(left, right) {
        return left + right;
    };
    const proxy = new Proxy(sum,twice);
    console.log(proxy(1,2));//6

当执行proxy()时,会被apply()拦截;code

has(target,key):用来拦截 hasProperty 操做,即判断对象是否具备某个属性;典型的操做就是 in 运算符。对象

const handler = {
                has(target,key) {
                    if(key[0] === '_') {
                        return false;
                    }
                    return key in target;
                }
     };
    const target = {_prop: 'foo', prop: 'foo'};
    const proxy = new Proxy(target,handler);
    console.log('_prop' in proxy);// false;

这里的代码中,若是原对象的属性名的第一个字符时下划线,proxy.has() 就会返回 false,从而不被 in 运算符发现。注意:has() 拦截的是hasProperty 操做,而不是 hasOwnProperty 操做,即它不判断属性来源。索引

this问题

虽然 proxy 能够代理针对目标对象的访问,但它不是目标对象的透明代理。不作任何拦截的状况下,也没法保证与目标对象的行为一致。主要缘由就是在Proxy 代理的状况下,目标对象内部的 this 会指向 Proxy 代理。

const target = {
                m: function () {
                    console.log(this === proxy)
                }
            }
            const handler = {};
            const proxy = new Proxy(target,handler);
            target.m()// false
            proxy.m()// true

上面代码中,一旦 proxy 代理 target.m,后者内部的 this 就指向了 proxy 而不是 target 了。

相关文章
相关标签/搜索