Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3.ES6详解-Proxy #15

Open
Geek-James opened this issue Jul 18, 2019 · 0 comments
Open

3.ES6详解-Proxy #15

Geek-James opened this issue Jul 18, 2019 · 0 comments
Labels

Comments

@Geek-James
Copy link
Owner

参考学习:

概述

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

下面是 Proxy 支持的拦截操作一览,一共 13 种。
  • get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo']。

  • set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或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)。

ES6原生提供了Proxy构造函数,用来生成Proxy实例

var proxy = new Proxy(target, handler);

Proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。

如果handler没有设置任何拦截,那就等同于直接通向原对象。

var target = {};
var handler = {};
var proxy = new Proxy(target,handler);
proxy.a = 'b'; 
console.log(target.a); // 'b'

还有一个技巧是将Proxy对象,设置到object.proxy属性,从而在object对象上调用.

var proxy = new Proxy({}, {
  get: function(target, property) {
    return 35;
  }
});

let obj = Object.create(proxy);
obj.time // 35
get()

get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。

get方法的用法,上文已经有一个例子,下面是另一个拦截读取操作的例子

let  perosn = {
    name:'james',
    age:26,
    profession:'software'
}

var proxy = new Proxy(perosn,{
    get:function(target,property) {
        if (property in target) {
            return target[property];
        } else {
            throw new ReferenceError("propertype\""+property + "\" does no exit" );
        }
    }
});

console.log(proxy.name,proxy.profession);
console.log(proxy.sex);

get方法可以通过Object来继承

set()

set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。

假定Person对象有一个age属性,该属性应该是一个不大于 200 的整数,那么可以使用Proxy保证age的属性值符合要求。

let  perosn = {
    name:'james',
    age:26,
    profession:'software'
}

var proxy = new Proxy(perosn,{
    get:function(target,property) {
        if (property in target) {
            return target[property];
        } else {
            throw new ReferenceError("propertype\""+property + "\" does no exit" );
        }
    },
    set:function(target,key,value) {
        if(key === 'age') {
            if(value>80) {
                throw ReferenceError("invail");
            } else {
                return target[key] = value;
            }
        } else {
            return target[key];
        }
    }
});
proxy.age = 60;
console.log(proxy.name,proxy.profession,proxy.age);
apply()

apply方法拦截函数的调用、call和apply操作。

apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。

var twice = {
    apply(target,ctx,agrs) {
        return Reflect.apply(...arguments)*2;
    }
};
function sum (a,b) {
    return a+b;
}
var proxy5 = new Proxy(sum,twice);

console.log(proxy5(1,3));  // 8
console.log(proxy5.apply(null,[1,3])); // 8

另外,直接调用Reflect.apply方法,也会被拦截。

Reflect.apply(proxy, null, [9, 10]) // 38
has()

has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。

has方法可以接受两个参数,分别是目标对象、需查询的属性名。

下面的例子使用has方法隐藏某些属性,不被in运算符发现。

let stu1 = {name: '张三', score: 59};
let stu2 = {name: '李四', score: 99};

let handler = {
  has(target, prop) {
    if (prop === 'score' && target[prop] < 60) {
      console.log(`${target.name} 不及格`);
      return false;
    }
    return prop in target;
  }
}

let oproxy1 = new Proxy(stu1, handler);
let oproxy2 = new Proxy(stu2, handler);

'score' in oproxy1
// 张三 不及格
// false

'score' in oproxy2
// true

for (let a in oproxy1) {
  console.log(oproxy1[a]);
}
// 张三
// 59

for (let b in oproxy2) {
  console.log(oproxy2[b]);
}
// 李四
// 99

上面代码中,has拦截只对in运算符生效,对for...in循环不生效,导致不符合要求的属性没有被for...in循环所排除。

@Geek-James Geek-James added the ES6 label Jul 19, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant