ES6学习系列——Proxy

Proxy 用于修改某些操作的默认行为,等于是在语言层面做出了修改,也就是对编程语言进行改动。具体来说,Proxy就是一种机制,用来拦截外界对目标对象的访问,可以对这些访问进行过滤或者改写,所以Proxy更像是目标对象的代理器。

1、ES6 原生提供Proxy构造函数,可以用来生成proxy实例:

(1)实例

let proxy = new Proxy(target, handler);
接收两个参数:
target 是要代理的目标对象;
handler 也是一个对象,用来定义拦截的具体行为;如果拦截具有多个操作,就可以这样定义handler {fn, ….}

(2)Proxy 拦截操作汇总,共13个:
  • get(target, propKey, receiver):拦截对象属性的读取,比如 proxy.foo
  • set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v ,返回一个布尔值;
  • has(target, propKey):拦截 propKey in proxy 的操作,返回一个布尔值;
  • deleteProperty(target, propKey):拦截 delete proxy[propKey] 的操作,返回一个布尔值;
  • ownKeys(target):拦截
    Object.getOwnPropertyNames(proxy)
    Object.getOwnPropertySymbols(proxy)
    Object.keys(proxy)
    for...in 循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys() 的返回结果仅包括目标对象自身的可遍历属性。
  • getOwnPropertyDescriptor(target, propKey): 拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  • defineProperty(target, propKey, propDesc):拦截
    Object.defineProperty(proxy, propKey, propDesc)
    Object.defineProperties(proxy, propDescs),返回一个布尔值。
  • 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 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)
(3)Proxy常用拦截方法详解(暂时就用到这两个,过后再补):
get ( ): 如果某个属性不可写或者不可配置,则该属性不能被访问,set()也同理;

如上所述,这个get() 方法用来拦截对目标对象的属性访问;还是上实例吧:

let idiot= {
    name: "Troyes",
    age: 39
};
let proxy = new Proxy(idiot, {
    get (target, property) {

        //我天,有个return怎么就用不了三元操作符?
        if (property in target){
            return target[property];
        }else{
            throw new ReferenceError("the property you want to get:"+ property +" does not exist!");
        }

        //老衲有妙招:就是有点烦,flag无论真假,都是return出去,容我以后再来想想能不能优化
        //return (property in target) ? (target[property]) : ( "the property you want to get:"+ property +" does not exist!");
    }
});
console.log(proxy.name, proxy.age); //Troyes 39
console.log(proxy.job); //ReferenceError: the property you want to get:job does not exist! 
set ( ):

这个方法用于拦截对某个属性的赋值操作,废话不都说,上实例:

let checker = {
    set (target, prop, val) {
        //验证设置的特定的属性值符不符合要求
        if (prop=== 'age') {
            if (Number.isInteger(val)) {
                if (val>300) {
                    throw new RangeError("Are you f**king kiding me?");
                }   
            }else{
                throw new TypeError("The age is not a integer.");
            }

        }

        //无要求的属性值,符合要求的age都直接存起来
        target[prop] = val;
    }
};

let Person = new Proxy({}, checker);

Person.age = 35;
console.log(Person.age); //35
Person.age = "123d"; //TypeError: The age is not a integer.
Person.age = 1000; //RangeError: Are you f**king kiding me?
Person.name = "troyes";
console.log(Person.name); //troyes
(4)关于 This

用了 Proxy 之后,proxy代理的 this 并非指向目标对象,而是指向自身proxy。所以这里就会有坑:某些原生对象的 this 指向如果不对,是无法访问到他的内部属性的;(解决方法下面有讲到)

let a = {
    toConsole () {
        if (this === proxy) {
            console.log("the 'this' of a change its pointing to proxy.");
        }else if (this === a) {
            console.log("waiting to chang its pointing.");
        }
    }
};

let proxy = null;
a.toConsole(); //waiting to chang its pointing.
proxy = new Proxy( a, {});
a.toConsole(); //waiting to chang its pointing. 在a中,a的this没变,还是a;
proxy.toConsole (); //the 'this' of a change its pointing to proxy. 但是在proxy中,this却已经指向了proxy;

有些原生对象的内部属性的this不对就会报错;那么知道坑在这里,怎么解决呢?
答案就是通过绑定this,写 handler 这个参数的时候,通过 bind(target) 直接把this 绑定到原生对象 target上 ;

let target = new Date();
let handler = {
    get (target, prop) {
        if (prop === 'getTime') {
            //直接把this绑定到target上
            return target.getTime.bind(target);
        }
        return Reflect.get(target, prop);   
    }

};
let proxy = new Proxy(target, handler);
let nowTime = proxy.getTime();
console.log(nowTime); //1536074540607
//加上这句,直接报错
proxy.getDay(); //TypeError: this is not a Date object. 

写到这里,索性再来说说 bind(obj) 这个方法吧;
正如上面说的,调用这个方法就会直接把 this 直接绑定到 obj 上;

var module = {
  x: 42,
  getX: function () {
    return this.x;
  }
}

var unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope, the this is pointing to window, so window.x is undefined
// expected output: undefined

var boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
//bind the this to module
// expected output: 42

猜你喜欢

转载自blog.csdn.net/qq_39798135/article/details/82388180
今日推荐