什么?JS也和迪丽热巴一样有专业替身?没听过的快来补补课...
什么?JavaScript和大名鼎鼎的迪丽热巴一样都有专业替身?没听过的快来补补课...
写在前面
这是设计模式系列的第二节,学习的是patterns.dev里设计模式中代理模式内容,由于是资料是英文版,所以我的学习笔记就带有翻译的性质,但并不是翻译,记录的是自己的学习过程和理解。
工作了多年了,在设计组件时往往关注业务逻辑的实现,而忽略了代码组织模式结构上的优化,一些本可以复用的代码却不知道怎么复用,或者在不熟悉复用逻辑的情况喜爱就直接复制一份,造成了一些代码的维护成本和体量的冗余。最近开始封装一些业务组件,想把设计模式使用进去,所以正好学习一番,感兴趣的朋友可以一起来探讨。
后续可能会专门针对每种设计模式设计一个比较适合的组件,敬请期待或催更,点个关注不迷路!
代理模式
基本释义
通过替身,拦截并控制目标对象和其他对象的互动。
没错,咱们JavaScript里专业替身代理模式,就是通过Proxy关键字实现的。
const person = {
name: "John Doe",
age: 42,
nationality: "American"
};
const personProxy = new Proxy(person, {});
交互实现:通过第二个控制器参数handler实现与被代理对象进行交互
const personProxy2 = new Proxy(person, {
get: (obj, prop) => {
console.log(`The value of ${prop} is ${obj[prop]}`);
},
set: (obj, prop, value) => {
console.log(`Changed ${prop} from ${obj[prop]} to ${value}`);
obj[prop] = value;
}
});
通过new一个Proxy对象,这个对象构造函数接收两个参数,第一个就是被代理的目标对象,第二个就是控制器handler,通常是获取或修改目标对象属性get和set方法。
替身的使用
替身的使用和被代理的对象的使用一样,直接调用就可以了。
personProxy.name;
personProxy.age = 43;
person.age = 45; // 被代理的对象依然可以直接使用
当然new Proxy构造函数的第二个控制器参数是必传的,如果是和替身的属性一一对应,没有其他操作,可以传个空对象{},否则的话可以重写get或set方法,处理特殊逻辑。
其中get方法有两个参数,第一个参数是被代理的Object本身,第二个参数是当前被使用的属性的名称,return返回值会直接生效,不return会默认拿取被代理对象的该属性值;
而set方法有三个参数,第一个参数是被代理的Object本身,第二个参数是当前被修改的属性名称,第三个参数是将要设置的值;重写set方法必须要重写修改逻辑obj[prop] = value,不重新修改逻辑,set的属性值无效,而且set必须return true,否则会报错;
最佳实践
代理模式通常用来校验属性值,比如类型、是否为空,是否非法等等,从而最大限度的保证数据的有效性;
const personProxy = new Proxy(person, {
get: (obj, prop) => {
if (!obj[prop]) {
console.log(
`Hmm.. this property doesn't seem to exist on the target object`
);
} else {
console.log(`The value of ${prop} is ${obj[prop]}`);
}
},
set: (obj, prop, value) => {
if (prop === "age" && typeof value !== "number") {
console.log(`Sorry, you can only pass numeric values for age.`);
} else if (prop === "name" && value.length < 2) {
console.log(`You need to provide a valid name.`);
} else {
console.log(`Changed ${prop} from ${obj[prop]} to ${value}.`);
obj[prop] = value;
}
}
});
Reflect映射
Reflect对象和Proxy构造函数的第二个参数handler控制器一样,通过get和set获取或者修改目标对象的属性;所以personProxy就可以改造为下面这种写法:
const personProxy = new Proxy(person, {
get: (obj, prop) => {
console.log(`The value of ${prop} is ${Reflect.get(obj, prop)}`);
},
set: (obj, prop, value) => {
console.log(`Changed ${prop} from ${obj[prop]} to ${value}`);
Reflect.set(obj, prop, value);
}
});
handler的get方法中,通过Reflect.get方法获取目标对象obj的prop属性的值;通过Reflect.set方法设置目标对象obj的prop属性的值为value。
总结
代理模式对于控制目标对象的行为是非常有用的。代理有很多使用场景,比如说验证,格式化,警告以及调试。
但是过度使用代理对象,或者是在handler控制器中做很耗性能的操作,就会很容易影响到你整个应用的性能表现。所以最好不要在高性能要求的代码中使用代理模式。
转载自:https://juejin.cn/post/7174418151440908302