用 JS 学透代理模式

86 阅读5分钟

**

前阵子想租房,打开租房软件翻了半天,看得眼花缭乱。后来朋友说不如找个中介,省事儿。抱着试试的心态联系了中介,没想到真省了不少事 —— 中介会帮我筛选符合预算的房源,预约看房时间,还能跟房东砍价。整个过程我几乎没跟房东直接打过交道,全靠中介在中间协调。

后来学代理模式的时候,突然发现这事儿跟代理模式简直一模一样!我(客户端)想租房(访问目标对象),但不想直接面对找房、议价这些麻烦事,于是找了中介(代理)。中介知道我要什么,也清楚房东(真实对象)的情况,帮我处理了各种杂事,最后让我顺利租到了房子。

这么一想,代理模式的核心好像就是:不想直接跟某个对象打交道的时候,找个 "中间人" 帮忙。这个中间人就是代理,它知道怎么跟真实对象沟通,还能帮我们处理一些额外的事情。

手把手写个简单的代理代码

光理解概念不够,还得动手写写代码。我来举个例子,就模拟刚才租房的场景。

首先得有个 "租房" 的约定(虽然 JavaScript 没有接口,但我们可以用函数或对象结构来模拟抽象主题),不管是房东还是中介,都得能处理租房这件事:

// 租房的抽象行为定义
// 在JavaScript中用函数模拟接口约定
function RentHouse() {}
RentHouse.prototype.rent = function() {
  throw new Error("子类必须实现rent方法");
};

然后是房东(真实主题),他的核心业务就是把房子租出去:

// 房东(真实主题)
class Landlord extends RentHouse {
  rent() {
    console.log("房东:房子租出去啦,每月3000元");
  }
}

最关键的来了 —— 中介(代理)。他得知道怎么处理租房流程,还得知道房东是谁。除了帮房东把房子租出去,中介还会做些额外的事,比如带看房、签合同:

// 中介(代理)
class Agent extends RentHouse {
  constructor(landlord) {
    super();
    this.landlord = landlord; // 持有房东的引用
  }
  rent() {
    console.log("中介:带客户看房");
    console.log("中介:和客户谈价格");
    this.landlord.rent(); // 调用房东的租房方法
    console.log("中介:签订租房合同");
  }
}

最后是我(客户端)租房的过程:

// 客户端代码
// 有个房东要租房
const landlord = new Landlord();
// 找个中介帮忙
const agent = new Agent(landlord);
// 通过中介租房
agent.rent();

运行这段代码,输出结果是:

中介:带客户看房
中介:和客户谈价格
房东:房子租出去啦,每月3000元
中介:签订租房合同

你看,我(客户端)只调用了中介的 rent () 方法,就完成了租房。中间那些繁琐的流程全是中介处理的,房东只负责把房子租出去这个核心操作。这就是代理模式最基本的用法!

静态代理和动态代理的区别

学到后面又遇到了静态代理和动态代理这两个概念,搞清楚它们的区别后也挺好理解的。

刚才写的中介例子就是静态代理 —— 中介类是我自己写的,在代码运行前就确定了它要代理哪个房东。如果小区里有 10 个房东,我就得写 10 个类似的中介类,每个对应一个房东,这也太麻烦了!

那动态代理就是来解决这个问题的。它能在程序运行的时候,动态创建一个代理对象,不管有多少个房东,都能用同一个代理逻辑来处理。就像小区里有个超级中介,不管哪个房东的房子,他都能帮着租出去,还不用提前知道具体是哪个房东。

在 JavaScript 中实现动态代理更简单,因为它有更灵活的对象操作能力。比如用 Proxy 对象就能轻松实现:

// 动态代理示例
function createAgent(landlord) {
  return new Proxy(landlord, {
    apply(target, thisArg, args) {
      console.log("中介:带客户看房");
      console.log("中介:和客户谈价格");
      const result = target.rent(...args);
      console.log("中介:签订租房合同");
      return result;
    }
  });
}
// 使用动态代理
const landlord = new Landlord();
const dynamicAgent = createAgent(landlord);
dynamicAgent.rent();

不过动态代理的思路稍微绕一点,涉及到对对象行为的动态拦截。我觉得先把静态代理吃透,再慢慢研究动态代理会更容易些。

什么时候该用代理模式?

学了半天,总得知道这东西在实际开发中能用到哪儿吧。结合教程和自己的理解,我总结了几个常见场景:

  • 想在调用某个方法前后加点额外操作:比如记录日志、检查权限。就像中介在租房前后要做的那些事。

  • 不想直接访问某个复杂对象:比如加载一个很大的图片,可以先用代理显示 "加载中",等真正准备好了再显示图片。

  • 需要控制访问权限:比如有些敏感接口,得先通过代理检查用户有没有权限访问。

容易踩的坑

作为过来人,提醒一下和我一样的新手朋友:

  1. 别把代理模式想复杂了,它本质就是 "中间人" 模式,生活中到处都是例子。

  2. 写代理的时候,尽量让代理对象和真实对象保持一致的接口(方法名和参数),不然调用的时候会出问题。

  3. 不要为了用代理而用代理,如果不需要额外操作,直接调用真实对象更简单。

最后,如果你不知道什么是代理模式,希望这篇文章能帮你少走点弯路。如果有理解不对的地方,欢迎大神们指点~