从C#到TypeScript - Proxy

从C#到TypeScript - Proxy

我们知道在C#中要实现代理功能需要自己来实现代理类,并且每个类需要不同的代理类,使用起来不方便,虽然借助一些AOP框架可以一定程度实现拦截,但毕竟框架级别的还是太重了。 现在ES6倒是出了个解决方案,Proxy是ES6推出的用于拦截操作的一层代理实现,TypeScript当然也同样支持,下面来看下Proxy是怎么用的。

Proxy使用

Proxy本身是一个类,可以通过new来实例化一个代理。

let p = new Proxy(target, handle)

Proxy有两个参数: target指所要代理的对象。 handle也是一个对象,对象里包含对target操作的拦截。 看个例子:

let obj = { name: 'brook' };
let p = new Proxy(obj, {
    get(target, property){
        return 'cnblogs';
    }
});

console.info(obj.name); // brook
console.info(p.name); // cnblogs

可以看到,p做为obj的代理,在handle里加了对目标对象的属性get操作进行拦截,所以第一次直接输出obj的name是'brook',用代理p输出就变成'cnblogs'了。 因为handle里对获取属性操作进行了重新定义。 get函数同样有两个参数,target仍然是操作对象,另一个property则是要访问的属性的名字。

Proxy可拦截的操作

  • get(target, propKey, receiver)
  • set(target, propKey, value, receiver)
  • apply(target, object, args)
  • defineProperty(target, propKey, propDesc)
  • deleteProperty(target, propKey)
  • has(target, propKey)
  • ownKeys(target)
  • construct(target, args)
  • getPrototypeOf(target)
  • setPrototypeOf(target, proto)
  • getOwnPropertyDescriptor(target, propKey)
  • isExtensible(target)
  • preventExtensions(target)

看过上一篇Reflect的有没有很熟,没错,Reflect里的操作Proxy里都同样有一份,这样在做Proxy的时候,如果要回到原始的结果,直接调用Reflect对应的操作就好。 接下来挑几个重要的看看。

get

get(target, propKey, receiver) 上面提到过get,不过没说第三个参数,其实receiver指的就是new出来的Proxy对象。

let obj = { name: 'brook' };
let p = new Proxy(obj, {
    get(target, property, receiver){
        console.info(receiver === p); // true
        return 'cnblogs'
    }
});
console.info(p.name);

再来个例子来看看get能做到什么程度,我们知道数组的索引不能为负数,现在我们通过Proxy来让数组来支持它:

let arr = ["b", "r", "o", "o", "k"];
let p = new Proxy(arr, {
    get(target, property){
        let index = Math.abs(Number(property));  // 取负数的绝对值
        return arr[index];
    }
});
console.info(arr[2]);  // 输出o
console.info(p[-2]);  //同样输出o

set

set(target, propKey, value, receiver) set用来拦截属性的赋值操作,比如number类型的数组,可以让它接受任何类型的值,当不是number的时候就给值0,当然这只是个不符合实际使用的功能演示:

let arr = new Array<number>();
let p = new Proxy(arr, {
    set(target, property, value, receiver){
        if(typeof value != 'number'){  // 不是number就设为0
            value = 0;
        }

        return Reflect.set(target, property, value);
    }
});

p[0] = 11;
p[1] = "brook";

console.info(arr[0]); // 11
console.info(arr[1]); // 0

现在前端MVVM很火,而用set就可以轻松做到设置属性值的同时更新Dom对象,实现绑定的效果。

apply

apply(target, object, args) 这可以拦截函数的调用,第一个和第三个参数的意思很明确,分别指函数和函数的参数。 第二个参数是指上下文的this,this的不同会可能导致函数里变量值的不同。

class Test1{
    text: string = 'test1';

    func(){
        console.info('call test1 func')
        console.info(`I am brook in ${this.text}`);
    }
}

class Test2{
    text: string = 'test2';

    func(){
        console.info('call test2 func')
        console.info(`I am brook in ${this.text}`);
    }
}

let t1 = new Test1();
let t2 = new Test2();

let p = new Proxy(t1.func, {
    apply(target, thisArg, args){
        Reflect.apply(target, t2, args);
    }
});

p();

上面代码输出信息如下:

call test1 func
I am brook in test2

也就是实际调用的还是Test1的func,所以第一条输出为call test1 func,虽然Proxy代理的是Test1的func,但实际执行时传的this是t2,所以函数里的this指向了Test2,取的也就是test2中要实现代理功能需要自己来实现代理类,并且每个类需要不同的代理类,使用起来不方便,虽然借助一些AOP框架可以一定程度实现拦截,但毕竟框架级别的还是太重了。

上面介绍了几个常用的,其他的意思也很明显就不多说了,Proxy的应用场景除了上面说过的MVVM外,还可以用在ORM中,把对象的行为映射到数据库中,还有数据访问的代理,总之想用到代理的可以考虑Proxy。 还有就是要记住Proxy不是透明代理,它有自己的this,使用时需要注意。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Scott_Mr 个人专栏

如何正确使用const(常量),define(宏)

29570
来自专栏小勇DW3

自己手动写代码实现数据库连接池

池:一个高级的集合体(集合存储元素 + 管理方式–>提高效率),是对外提供同一种类型对象的集合,如(线程池、数据库连接池)  特性:复用性(每条连接可重复使用)...

18030
来自专栏老马说编程

(72) 显式条件 / 计算机程序的思维逻辑

上节我们介绍了显式锁,本节介绍关联的显式条件,介绍其用法和原理。显式条件也可以被称做条件变量、条件队列、或条件,后文我们可能会交替使用。 用法 基本概念和方法...

20160
来自专栏哈雷彗星撞地球

Runtime系列(一)-- 基础知识

众所周知,Objective-C 是一种运行时语言。运行时怎么来体现的呢?比如一个对象的类型确定,或者对象的方法实现的绑定都是推迟到软件的运行时才能确定的。而运...

9020
来自专栏冰霜之地

神经病院Objective-C Runtime住院第二天—消息发送与转发

现在越来越多的app都使用了JSPatch实现app热修复,而JSPatch 能做到通过 JS 调用和改写 OC 方法最根本的原因是 Objective-C 是...

15330
来自专栏李航的专栏

Shell 主要逻辑源码级分析:SHELL 运行流程 (1)

分享一下在学校的时候分析shell源码的一些收获,帮助大家了解shell的一个工作流程,从软件设计的角度,看看shell这样一个历史悠久的软件的一些设计优点和缺...

2.2K00
来自专栏進无尽的文章

实践-小细节Ⅷ

const 这个单词翻译成中文是“常量”的意思。在程序中我们知道“常量”的值是不能变的,固定的。所以const关键字的作用就是:

12840
来自专栏marsggbo

python多线程学习笔记(超详细)

python threading 多线程 一. Threading简介 首先看下面的没有用Threading的程序 import threading,time ...

24480
来自专栏大内老A

ASP.NET MVC基于标注特性的Model验证:ValidationAttribute

通过前面的介绍我们知道ModelValidatorProviders的静态只读Providers维护着一个全局的ModelValidatorProvider列表...

231100
来自专栏青玉伏案

ReactiveSwift源码解析(五) SignalProtocol的observe()、Map、Filter延展实现

上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于《Signal中的静态属性静态方法以及面向协议扩展》。并且聊了S...

28070

扫码关注云+社区

领取腾讯云代金券