Skip to content

实践 ES6 Proxy & Reflect #79

@ChaoCSSun

Description

@ChaoCSSun

1.why

Why? 为什么提出Proxy?
ES5
提供了 Object.defineProperty() 存在两种的属性描述: 数据描述
configurable,
enumerable,
value:属性值
writeable
存取描述 getter/setter 方法 没有就是undefined
get:/ Set: method

例如: ES5 Getter/Setter

let hero = {
 name: '赵Sam',
hp:100,
sp:90
}

然后使用 Object.defineProperty() 对其具有的一些属性进行修改

Object.defineProperty(hero, 'hp', {
  set (val) {
    console.log(`Set hp to ${val}`);
    return val;
  }
})

hero.hp = '200';

这样是可以简单的修改
如果在修改下英雄属性

let hero = {
name:'赵云',
hp: 100,
sp: 100,
equipment:['马','长枪']
}
这需要下面

_Object.defineProperty(hero.equipment, 'push', {
value () {
this[this.length] = arguments[0];
}
})

hero.equipment.push('佩剑');
console.log(hero.equipment);_

由此可以看到 对象属性的变换可以 通过get set()方法最终和改变,但对于数组则需要使用Value()显然这不是 好的方法

由此提出了Proxy 概念 ES6

2.ES6 Proxy

是什么?
Proxy 意思是代理,即访问对象之前建立一道 “拦截”, 任何访问该对象的操作之前都会通过这道“拦截”, 即执行Proxy的定义方法

基本用法:

let pro = new Proxy(target, handler);

new Proxy()表示生成一个Proxy实例
target参数表示所要拦截的目标对象
handler参数也是一个对象,用来定制拦截行为

handler

Proxy支持13种拦截行为(handle)

实例1

let hero = {
  name: "赵云",
  age: 25
}

let handler = {}

let heroProxy = new Proxy(hero, handler);

console.log(heroProxy.name);
// --> 赵云
heroProxy.name = "黄忠";
console.log(heroProxy.name);
// --> 黄忠

解析:

创建hero对象为所要拦截的对象;

拦截操作对象handler为空,未对拦截对象设定拦截方法;

该情况下heroProxy直接指向原对象target,访问heroProxy等同于访问target,所以结果为target中的结果。

在我们对Proxy有了一定了解后,可以尝试解决上一节的问题。

首先,还是定义一个英雄:

**let hero = {
name:'赵云',
hp: 100,
sp: 100,
equipment:['马','长枪']
}
接着,定义一个handler:

let handler = {
set(target, property, value) {
console.log(hero's ${property} change to ${value});
target[property] = value;
return true;
}
}
然后,修改英雄的hp值

let heroProxy = new Proxy(hero, handler);
heroProxy.hp = 200;
// --> hero's hp change to 200
console.log(hero.hp);
// --> 200
最后,同样把“佩剑”添加到英雄的装备中

let heroProxy = new Proxy(hero.equipment, handler);
heroProxy.push('佩剑');
// --> hero's 2 change to 佩剑
// --> hero's length change to 3
console.log(hero.equipment);
// --> ["马", "长枪", "佩剑"]**
可以发现,heroProxy.push('佩剑');触发了两次set,原因是push即修改了hero.equipment的内容,又修改了hero.equipment的length

3 Reflect:

如果了解 proxy之后,若需要在Proxy 内部调用对象的默认行为 怎么实现?
Reflect 就是为了解决操作对象而提供的新的API

基本特点
只要Proxy对象具有的代理方法,Reflect对象全部具有,以静态方法的形式存在。这些方法能够执行默认行为,无论Proxy怎么修改默认行为,总是可以通过Reflect对应的方法获取默认行为。
修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。
让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。
静态方法
Reflect对象一共有 13 个静态方法(匹配Proxy的13种拦截行为)。

Reflect.apply(target, thisArg, args)
Reflect.construct(target, args)
Reflect.get(target, name, receiver)
Reflect.set(target, name, value, receiver)
Reflect.defineProperty(target, name, desc)
Reflect.deleteProperty(target, name)
Reflect.has(target, name)
Reflect.ownKeys(target)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)
大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的。

实例
下面通过3个实例来对比Object对象方法与Reflect对象方法。

实例1
//Object对象方法
try {
Object.defineProperty(target, name, property);
} catch (e) {
console.log("error");
}

//Reflect对象方法
if (Reflect(target, name, property)) {
console.log("success");
} else {
console.log("error")
}

reference:

https://zhuanlan.zhihu.com/p/60126477

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions