提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。

微信截图_20210525225551.png

  1. // 远程服务接口。
  2. interface ThirdPartyTVLib is
  3. method listVideos()
  4. method getVideoInfo(id)
  5. method downloadVideo(id)
  6. // 服务连接器的具体实现。该类的方法可以向腾讯视频请求信息。请求速度取决于
  7. // 用户和腾讯视频的互联网连接情况。如果同时发送大量请求,即使所请求的信息
  8. // 一模一样,程序的速度依然会减慢。
  9. class ThirdPartyTVClass implements ThirdPartyTVLib is
  10. method listVideos() is
  11. // 向腾讯视频发送一个 API 请求。
  12. method getVideoInfo(id) is
  13. // 获取某个视频的元数据。
  14. method downloadVideo(id) is
  15. // 从腾讯视频下载一个视频文件。
  16. // 为了节省网络带宽,我们可以将请求结果缓存下来并保存一段时间。但你可能无
  17. // 法直接将这些代码放入服务类中。比如该类可能是第三方程序库的一部分或其签
  18. // 名是`final(最终)`。因此我们会在一个实现了服务类接口的新代理类中放入
  19. // 缓存代码。当代理类接收到真实请求后,才会将其委派给服务对象。
  20. class CachedTVClass implements ThirdPartyTVLib is
  21. private field service: ThirdPartyTVLib
  22. private field listCache, videoCache
  23. field needReset
  24. constructor CachedTVClass(service: ThirdPartyTVLib) is
  25. this.service = service
  26. method listVideos() is
  27. if (listCache == null || needReset)
  28. listCache = service.listVideos()
  29. return listCache
  30. method getVideoInfo(id) is
  31. if (videoCache == null || needReset)
  32. videoCache = service.getVideoInfo(id)
  33. return videoCache
  34. method downloadVideo(id) is
  35. if (!downloadExists(id) || needReset)
  36. service.downloadVideo(id)
  37. // 之前直接与服务对象交互的 GUI 类不需要改变,前提是它仅通过接口与服务对
  38. // 象交互。我们可以安全地传递一个代理对象来代替真实服务对象,因为它们都实
  39. // 现了相同的接口。
  40. class TVManager is
  41. protected field service: ThirdPartyTVLib
  42. constructor TVManager(service: ThirdPartyTVLib) is
  43. this.service = service
  44. method renderVideoPage(id) is
  45. info = service.getVideoInfo(id)
  46. // 渲染视频页面。
  47. method renderListPanel() is
  48. list = service.listVideos()
  49. // 渲染视频缩略图列表。
  50. method reactOnUserInput() is
  51. renderVideoPage()
  52. renderListPanel()
  53. // 程序可在运行时对代理进行配置。
  54. class Application is
  55. method init() is
  56. aTVService = new ThirdPartyTVClass()
  57. aTVProxy = new CachedTVClass(aTVService)
  58. manager = new TVManager(aTVProxy)
  59. manager.reactOnUserInput()
  1. /**
  2. * The Subject interface declares common operations for both RealSubject and the
  3. * Proxy. As long as the client works with RealSubject using this interface,
  4. * you'll be able to pass it a proxy instead of a real subject.
  5. */
  6. interface Subject {
  7. request(): void;
  8. }
  9. /**
  10. * The RealSubject contains some core business logic. Usually, RealSubjects are
  11. * capable of doing some useful work which may also be very slow or sensitive -
  12. * e.g. correcting input data. A Proxy can solve these issues without any
  13. * changes to the RealSubject's code.
  14. */
  15. class RealSubject implements Subject {
  16. public request(): void {
  17. console.log('RealSubject: Handling request.');
  18. }
  19. }
  20. /**
  21. * The Proxy has an interface identical to the RealSubject.
  22. */
  23. class Proxy implements Subject {
  24. private realSubject: RealSubject;
  25. /**
  26. * The Proxy maintains a reference to an object of the RealSubject class. It
  27. * can be either lazy-loaded or passed to the Proxy by the client.
  28. */
  29. constructor(realSubject: RealSubject) {
  30. this.realSubject = realSubject;
  31. }
  32. /**
  33. * The most common applications of the Proxy pattern are lazy loading,
  34. * caching, controlling the access, logging, etc. A Proxy can perform one of
  35. * these things and then, depending on the result, pass the execution to the
  36. * same method in a linked RealSubject object.
  37. */
  38. public request(): void {
  39. if (this.checkAccess()) {
  40. this.realSubject.request();
  41. this.logAccess();
  42. }
  43. }
  44. private checkAccess(): boolean {
  45. // Some real checks should go here.
  46. console.log('Proxy: Checking access prior to firing a real request.');
  47. return true;
  48. }
  49. private logAccess(): void {
  50. console.log('Proxy: Logging the time of request.');
  51. }
  52. }
  53. /**
  54. * The client code is supposed to work with all objects (both subjects and
  55. * proxies) via the Subject interface in order to support both real subjects and
  56. * proxies. In real life, however, clients mostly work with their real subjects
  57. * directly. In this case, to implement the pattern more easily, you can extend
  58. * your proxy from the real subject's class.
  59. */
  60. function clientCode(subject: Subject) {
  61. // ...
  62. subject.request();
  63. // ...
  64. }
  65. console.log('Client: Executing the client code with a real subject:');
  66. const realSubject = new RealSubject();
  67. clientCode(realSubject);
  68. console.log('');
  69. console.log('Client: Executing the same client code with a proxy:');
  70. const proxy = new Proxy(realSubject);
  71. clientCode(proxy);

ES6 Proxy 使用

  • 拦截和监听外部对对象的访问
  • 降低函数或类的复杂度
  • 在复杂操作前对操作进行校验或对所需资源进行管理

    参考资料

  1. 代理模式
  2. Proxy 和 Reflect
  3. Looking at All 13 JavaScript Proxy Traps
  4. 实例解析ES6 Proxy使用场景
  5. A practical guide to Javascript Proxy
  6. 带你彻底搞懂Vue3的Proxy响应式原理!TypeScript从零实现基于Proxy的响应式库。