js的代理与反射

代理与反射-反射

一 反射的基本使用

1.1 反射概念

在 JS 运行时,能够知道这个对象的成员,并能够调用这些成员,这种动态获取信息、动态调用对象方法的功能称为反射。

反射 API 范围很广,可以用于代理中的捕获处理程序。大多的反射 API 方法在 Object 类型上都有对应的方法。相比 Object 直接操作,反射更适合用于细粒度的对象控制。

1.2 反射的状态标记

很多反射方法返回一个布尔值,表示意图执行的操作是否成功,称作“状态标记”的布尔值。有时候,状态标记比那些返回修改后的对象或者抛出错误(取决于方法)的反射 API 方法更有用。

例如在定义新属性时如果发生问题, Reflect.defineProperty()会返回 false,而不是抛出错误,以下是使用 Object 原生方法的实现该需求:

// 使用 Object 原生方法:
const o = {}

try {
  Object.defineProperty(o, 'foo', { value: 'bar' })
  console.log('success')
} catch (e) {
  console.log('failure')
}

可以使用反射 API 对下面的代码进行重构:

const o = {}

if (Reflect.defineProperty(o, 'foo', { value: 'bar' })) {
  console.log('success')
} else {
  console.log('failure')
}

以下反射方法都会提供状态标记:

Reflect.defineProperty()
Reflect.preventExtensions()
Reflect.setPrototypeOf()
Reflect.set()
Reflect.deleteProperty()

1.3 替代操作符的方法

以下反射方法提供只有通过操作符才能完成操作:

// 可以替代对象属性访问操作符
Reflect.get()

// 可以替代=赋值操作符
Reflect.set()

// 可以替代 in 操作符或 with()
Reflect.has()

// 可以替代 delete 操作符
Reflect.deleteProperty()

// 可以替代 new 操作符
Reflect.construct()

1.4 函数安全应用

在通过 apply 方法调用函数时,被调用的函数可能也定义了自己的 apply 属性(虽然可能性极小)。为绕过这个问题,可以使用定义在 Function 原型上的 apply 方法,比如:

Function.prototype.apply.call(myFunc, thisVal, argumentList)

使用反射可以解决上述问题:

Reflect.apply(myFunc, thisVal, argumentsList)

二 代理与反射的配合

2.1 优雅地书写捕获器

代理的捕获器并不是都像 get() 这么简单,手动重建原始行为是不现实的,可以通过调用全局 Reflect 对象上(封装了原始行为)的同名方法来轻松重建。

处理程序对象中所有可以捕获的方法都有对应的反射( Reflect) API 方法。这些方法与捕获器拦截的方法具有相同的名称和函数签名,而且也具有与被拦截方法相同的行为。因此,使用反射 API 也可以像下面这样定义出空代理对象:

const target = {
  foo: 'bar',
}

const handler = {
  get() {
    console.log('handler override')
    return Reflect.get //  Reflect.get(...arguments) 的简写
  },
}

const proxy = new Proxy(target, handler)

console.log(proxy.foo) // 'handler override' bar

如果直接将全局反射对象作为处理程序对象,那么所有可捕获的方法将被捕获:

const target = {
  foo: 'bar',
}

const proxy = new Proxy(target, Reflect)

console.log(proxy.foo) // bar

反射 API 为开发者准备好了样板代码,在此基础上开发者可以用最少的代码修改捕获的方法。 比如,下面的代码在某个属性被访问时,会对返回的值进行一番修饰:

const target = {
  foo: 'bar',
  baz: 'qux',
}

const handler = {
  get(trapTarget, property, receiver) {
    let decoration = ''
    if (property === 'foo') {
      decoration = '!!!'
    }
    return Reflect.get + decoration
  },
}

const proxy = new Proxy(target, handler)
console.log(proxy.foo) // bar!!!
console.log(target.foo) // bar
console.log(proxy.baz) // qux
console.log(target.baz) // qux

2.2 代理另外一个代理

代理可以拦截反射 API 的操作,而这意味着完全可以创建一个代理,通过它去代理另一个代理。这样就可以在一个目标对象之上构建多层拦截网:

const target = {
  foo: 'bar',
}

const firstProxy = new Proxy(target, {
  get() {
    console.log('first proxy')
    return Reflect.get(...arguments)
  },
})

const secondProxy = new Proxy(firstProxy, {
  get() {
    console.log('second proxy')
    return Reflect.get(...arguments)
  },
})

// second proxy   first proxy  bar
console.log(secondProxy.foo)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值