我正在参加「掘金·启航计划」
代理模式,是一种常见的设计模式,它为其他对象提供一种代理以控制对这个对象的访问。在 JavaScript 中,代理模式可以直接使用 ES6 中的 Proxy 对象来实现。也可以像下面的例子中。手动实现代理对象。本文将介绍代理模式的概念。代理对象和本体对象保持接口的一致性,就可以实现代理模式;接下来将以多种方式实现代理模式。
概念
代理模式:它为其他对象提供一种代理以控制对这个对象的访问。代理对象作为被代理对象的中介,可以拦截并处理被代理对象的操作,以及为被代理对象提供额外的功能。
原理
代理模式中的代理对象和被代理对象都必须实现相同的接口,这样任何使用被代理对象的地方都可以用代理对象代替。在 TS 中,我们可以使用抽象类或接口来定义代理和被代理对象的共用接口。
以下是一个使用抽象类来定义代理和被代理对象的共用接口的例子:
Subject为公共接口,代理对象和本体都要实现该接口。这样对外就保持了一致性
abstract class Subject {
abstract request(): void;
}
class RealSubject extends Subject {
request() {
console.log('RealSubject request');
}
}
class MyProxy extends Subject {
private realSubject: RealSubject | null = null;
request() {
if (this.realSubject === null) {
this.realSubject = new RealSubject();
}
this.realSubject.request();
}
}
const proxy = new MyProxy();
proxy.request(); // 输出:RealSubject request
以下是一个使用代理实现图片预加载的例子: 我们将分别使用多种语法来实现,这也说明语法实现设计模式的语法并不是唯一的
- 这个例子我们直接使用es5的语法来实现
const myImage = (function () {
const imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc(src: string) {
imgNode.src = src;
},
};
})();
const proxyImg = (function () {
const img = new Image();
img.onload = () => {
myImage.setSrc(img.src);
};
return {
setSrc(src: string) {
myImage.setSrc('../loading.gif');
img.src = src;
},
};
})();
proxyImg.setSrc('http://example.com/image.jpg');
- 我们使用ES6的proxy语法来来实现
const myImage = (function () {
const imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc(src: string) {
imgNode.src = src;
},
};
})();
const proxyImg = new Proxy(myImage, {
get(target, property) {
return function (src: string) {
console.log(property)
target.setSrc('../loading.gif');
const img = new Image();
img.onload = () => {
target.setSrc(img.src);
};
img.src = src;
};
},
});
proxyImg.setSrc('http://example.com/image.jpg');
- 使用ts的语法来实现
abstract class Subject {
abstract setSrc(src: string): void;
}
class MyImage extends Subject {
private imgNode: HTMLImageElement;
constructor() {
super();
this.imgNode = document.createElement('img');
document.body.appendChild(this.imgNode);
}
setSrc(src: string) {
this.imgNode.src = src;
}
}
class ProxyImage extends Subject {
private myImage: MyImage | null = null;
setSrc(src: string) {
if (this.myImage === null) {
this.myImage = new MyImage();
}
this.myImage.setSrc('../loading.gif');
const img = new Image();
img.onload = () => {
this.?.setSrc(img.src);
};
img.src = src;
}
}
const proxyImg = new ProxyImage();
proxyImg.setSrc('http://example.com/image.jpg');
代理模式还可以实现很多功能,比如vue的实现原理就是代理对象,进行响应式通知;这里就不一一展开了
对其他设计模式有兴趣的同学可以看看这个仓库