# 代理与反射

## Reflect

某些情况下应该优先使用反射 API，这是有一些理由的。

1、反射 API 与对象 API 在使用反射 API 时，要记住:

* 反射 API 并不限于捕获处理程序;
* 大多数反射 API 方法在 Object 类型上有对应的方法。 通常，Object 上的方法适用于通用程序，而反射方法适用于细粒度的对象控制与操作。

2、状态标记 很多反射方法返回称作“状态标记”的布尔值，表示意图执行的操作是否成功。有时候，状态标记 比那些返回修改后的对象或者抛出错误(取决于方法)的反射 API 方法更有用。例如，可以使用反射 API 对下面的代码进行重构:

```js
// 初始代码
const o = {};
 try {
   Object.defineProperty(o, 'foo', 'bar');
   console.log('success');
 } catch(e) {
   console.log('failure');
}
```

在定义新属性时如果发生问题，Reflect.defineProperty()会返回 false，而不是抛出错误。 因此使用这个反射方法可以这样重构上面的代码: // 重构后的代码

```js
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()`

3、 用一等函数替代操作符 以下反射方法提供只有通过操作符才能完成的操作。

* `Reflect.get()`:可以替代对象属性访问操作符。
* `Reflect.set()`:可以替代=赋值操作符。
* `Reflect.has()`:可以替代 in 操作符或 with()。
* `Reflect.deleteProperty()`:可以替代 delete 操作符。
* `Reflect.construct()`:可以替代 new 操作符。

4、 安全地应用函数 在通过 `apply` 方法调用函数时，被调用的函数可能也定义了自己的 `apply` 属性(虽然可能性极小)。 为绕过这个问题，可以使用定义在 Function 原型上的 `apply` 方法，比如: `Function.prototype.apply.call(myFunc, thisVal, argumentList)`; 这种可怕的代码完全可以使用 `Reflect.apply` 来避免: `Reflect.apply(myFunc, thisVal, argumentsList)`;
