在观察者模式上的运用
有一个对象,是观察者,它用于观察另一个对象的属性值变化,当属性值变化后会收到一个通知,可能会做一些事
没有实现代理之前的实现方式
function observer(target) {
const div = document.getElementById("container");
const ob = {};
const props = Object.keys(target);
for (const prop of props) {
// ob[prop] = target[prop];
Object.defineProperty(ob, prop, {
get() {
return target[prop];
},
set(val) {
target[prop] = val;
render();
},
enumerable: true
})
}
render();
function render() {
let html = "";
for (const prop of Object.keys(ob)) {
html += `<p>
<span>${prop}:</span>
<span>${ob[prop]}</span>
</p>`
}
div.innerHTML = html;
}
return ob;
}
const target = {
a: 1,
b: 2
}
const obj = observer(target);
这里ob和target是两个对象,浪费内存空间 另一点:
虽说用函数把两个对象进行了关联,但再关联之后target再添加属性 例如c:20则obj.c = 5,这一操作上面写法的观察者模式将无法关联到。
而用Proxy则相当于在target对象上覆盖一层,不另开辟空间,而且target后续的添加属性也会很好的关联起来:
function observer(target) {
const div = document.getElementById("container");
const proxy = new Proxy(target, {
set(target, prop, value) {
Reflect.set(target, prop, value);
render();
},
get(target, prop) {
return Reflect.get(target, prop);
}
})
render();
function render() {
let html = "";
for (const prop of Object.keys(target)) {
html += `<p>
<span>${prop}:</span>
<span>${target[prop]}</span>
</p>`
}
div.innerHTML = html;
}
return proxy; //target
}
const target = {
a: 1,
b: 2
}
const obj = observer(target);
二 偷懒的构造函数
class User {
constructor(firstName, lastName, age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
}
function ConstructorProxy(Class) {
return new Proxy(Class, {
construct(target, argumentsList) {
console.log(argumentsList)
return Reflect.construct(target, argumentsList)
}
})
}
const UserProxy = ConstructorProxy(User);
new UserProxy();
const obj = new UserProxy("袁", "进", 18);
console.log(obj);
这段代码实现的功能,constructor里的那些this.firstName = firstName;
的语句,很多很繁琐,在一些场景下,我们可以通过Proxy在底层实现里把这一工作做了,而实现一些“偷懒”:
class User {
// constructor(firstName, lastName, age) {
// this.firstName = firstName;
// this.lastName = lastName;
// this.age = age;
// }
}
function ConstructorProxy(Class, ...propNames) {
return new Proxy(Class, {
construct(target, argumentsList) {
const obj = Reflect.construct(target, argumentsList)
propNames.forEach((name, i) => {
obj[name] = argumentsList[i];
})
return obj;
}
})
}
const UserProxy = ConstructorProxy(User, "firstName", "lastName", "age");
new UserProxy();
const obj = new UserProxy("袁", "进", 18);
console.log(obj);
这一做法可以轻松运用到别的类。
class Monster {
}
const MonsterProxy = ConstructorProxy(Monster, "attack", "defence", "hp", "rate", "name")
const m = new MonsterProxy(10, 20, 100, 30, "怪物")
console.log(m);
可验证的函数参数
有些场景需要验证函数的参数,这样抽离出来的验证方法,用Proxy实现如下:
求两数之后的函数,需要验证两参数是否是number类型
function sum(a, b) {
return a + b;
}
function validatorFunction(func, ...types) {
const proxy = new Proxy(func, {
apply(target, thisArgument, argumentsList) {
types.forEach((t, i) => {
const arg = argumentsList[i];
if (typeof arg !== t) {
throw new TypeError(`第${i+1}个参数${argumentsList[i]}不满足类型${t}`);
}
})
return Reflect.apply(target, thisArgument, argumentsList);
}
})
return proxy;
}
const sumProxy = validatorFunction(sum, "number", "number")
console.log(sumProxy("1", "2"));
不用Proxy时,常用一个函数来实现这一功能
function validatorFunction(func, ...types) {
return function(...argumentsList) {
types.forEach((t, i) => {
const arg = argumentsList[i];
if (typeof arg !== t) {
throw new TypeError(`第${i+1}个参数${argumentsList[i]}不满足类型${t}`);
}
})
return func(...argumentList)
}
}
const sumProxy = validatorFunction(sum, "number", "number")
console.log(sumProxy("1", "2"));
多写一个函数增加了内存开销,代理不会增加内存开销。显然Proxy实现这一功能时有很多优势。