ES6 详解 Proxy 拦截器/代理

发布于 2021-12-15 20:16:52 字数 5468 浏览 1161 评论 0

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 循环所排除。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

夜司空

暂无简介

文章
评论
584 人气
更多

推荐作者

夢野间

文章 0 评论 0

doggiejohn

文章 0 评论 0

就此别过

文章 0 评论 0

初见终念

文章 0 评论 0

qq_rvKjBH

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文