亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定

這就是所謂的代理模式

標簽:
設計模式

在我的之前的文章里,我探讨了各种创建模式,这些模式关注的是如何创建对象。现在,让我们深入探讨结构型模式,这些模式关注的是对象和类如何组合成更大的结构,同时保持灵活性和高效。我们先来看看代理模式

JavaScript中的代理设计模式——一种设计模式,用于间接地访问对象

代理设计模式是一种结构型设计模式,它提供一个对象来代表另一个对象。它充当一个中介,控制对实际对象的访问,并添加诸如延迟加载、日志记录、访问控制或缓存之类的功能,而不修改原始对象的代码。

在 JavaScript 中,代理 是通过 Proxy 对象提供的内置功能,允许您为诸如属性访问、赋值和函数调用等基本操作定义自己想要的行为

什么时候我们需要用到代理模式?

代理模式尤其有用,尤其是在以下几个情况:

  • 懒惰初始化:您希望延迟创建资源密集型对象,直到需要它时。
  • 访问控制:您需要控制对对象的访问,例如,限制未经授权的访问或根据条件限制操作的执行。
  • 日志记录:对对象的操作进行记录(例如,访问属性或调用方法)。
  • 缓存:您希望缓存高成本操作的结果以避免冗余计算。
代理模式的组成部分
  1. 主题: 定义了真实对象和代理共有的操作的接口。
  2. 真实对象: 执行实际工作的实际对象。
  3. 代理: 控制对真实对象访问的中介。
比如说:

想象你有一幅大型画作想展示给客人,但它很重,每次从储藏室搬出来都要花很长时间。为了避免每次都要等这么长时间,你决定用这幅画的小明信片快速展示给客人,让他们在等待真正画作被拿出来的时候有东西看。

在这个类比中:

  • 大画是真正的对象(就像一个加载慢的图像)。
  • 明信片是替代品(一个轻便的替代品,直到真正的画准备好了)。
  • 一旦真正的画准备好,你就把那幅真的画展示给客人看。

现实世界的比喻:

可以把房地产经纪人想象成一个代理。当你想要购买房子时,你并不会立即去参观每一栋房子。相反,房地产经纪人会先向你展示房子的照片和描述。只有当你决定真的要看房时,经纪人会安排你去看房子。

实际案例:图片加载(虚拟代理模式)

让我们以 web 应用中图片加载为例,希望在用户请求时再加载图片(即延迟加载),这时代理服务器可以先作为占位符,直到真正图片加载完成。

这里是如何用 JavaScript 实现代理设计模式的方法。

示例:图像加载代理

    // 步骤 1:真实对象
    class RealImage {
      constructor(filename) {
        this.filename = filename;
        this.loadImageFromDisk();
      }

      loadImageFromDisk() {
        console.log(`从磁盘加载 ${this.filename}...`);
      }

      display() {
        console.log(`显示 ${this.filename}`);
      }
    }

    // 步骤 2:代理图像
    class ProxyImage {
      constructor(filename) {
        this.realImage = null; // 尚未加载实际图像
        this.filename = filename;
      }

      display() {
        if (this.realImage === null) {
          // 只在需要时加载实际图像
          this.realImage = new RealImage(this.filename);
        }
        this.realImage.display(); // 显示实际图像
      }
    }

    // 步骤 3:使用代理显示图像
    const image = new ProxyImage("photo.jpg");
    image.display(); // 首次加载并显示图像
    image.display(); // 再次显示图像(已加载)

全屏 全屏退出

解释:

1). 真正的图:

  • RealImage类表示实际的图像。
  • 它接收一个文件名作为输入参数,并通过 loadImageFromDisk 方法来模拟从磁盘加载图像的过程。
  • 加载完成后,调用 display 方法来显示图像。

2). 代理图片:

  • ProxyImage类作为RealImage的替代物。它不会立即加载真实的图像。
  • 它保留对真实图像的引用(但最初是null,因为真实图像还未加载)。
  • 当你调用代理的display方法时,它会检查是否已加载真实图像。如果没有加载,它会先加载真实图像,再显示。

3). 用法:

  • 当我们创建一个 ProxyImage 的实例时,实际图像尚未加载(因为加载它会消耗大量资源)。
  • 第一次调用 display 方法时,代理会使用 RealImage 类加载图像并显示它。
  • 第二次调用 display 方法时,由于图像已经加载,它只会显示而不会重新加载图像。
自带的 Proxy 对象

ES6 代理使用一个构造器,这个构造器接受一个目标对象和一个处理函数作为参数。

    const 代理 = new Proxy(目标, 处理器)

全屏模式 退出全屏

在这里,target 表示代理被应用的对象,而 handler 是一个定义了代理对象行为的对象。

处理器对象包含一系列可选的陷阱方法,这些方法具有预定义名称(例如 apply、get、set 和 has 等),当在代理实例上执行相应操作时,这些方法将被自动调用。

让我们通过使用内置的代理功能来实现一个计算器功能,从而更好地理解这一点。

    // 步骤1:定义具有方法的Calculator类
    class Calculator {
      constructor() {
        this.result = 0;
      }

      // 方法:加法
      add(a, b) {
        this.result = a + b;
        return this.result;
      }

      // 方法:减法
      subtract(a, b) {
        this.result = a - b;
        return this.result;
      }

      // 方法:乘法
      multiply(a, b) {
        this.result = a * b;
        return this.result;
      }

      // 方法:除法
      divide(a, b) {
        if (b === 0) throw new Error("除数不能为零。");
        this.result = a / b;
        return this.result;
      }
    }

    // 步骤2:创建一个代理处理器以拦截操作
    const handler = {
      // 拦截'get'操作以确保访问方法
      get(target, prop, receiver) {
        if (prop in target) {
          console.log(`访问属性:${prop}`);
          return Reflect.get(target, prop, receiver); // 安全地访问属性
        } else {
          throw new Error(`属性"${prop}"不存在。`);
        }
      },

      // 拦截'set'操作以防止修改
      set(target, prop, value) {
        throw new Error(`无法修改属性 "${prop}"。这是因为计算器是不可变的。`);
      }
    };

    // 步骤3:创建一个继承Calculator原型的代理实例
    const calculator = new Calculator(); // 原始计算器对象
    const proxiedCalculator = new Proxy(calculator, handler); // 包裹计算器的代理

    // 步骤4:使用代理实例
    try {
      console.log(proxiedCalculator.add(5, 3)); // 输出:8
      console.log(proxiedCalculator.multiply(4, 2)); // 输出:8
      console.log(proxiedCalculator.divide(10, 2)); // 输出:5

      // 尝试直接通过代理访问原型(应该输出 true)
      console.log(proxiedCalculator.__proto__ === Calculator.prototype); // 输出:true

      // 尝试修改属性(应该会抛出异常)
      proxiedCalculator.result = 100; // 异常:无法修改属性 "result"。这是因为计算器是不可变的。
    } catch (error) {
      console.error(error.message); // 输出:无法修改属性 "result"。这是因为计算器是不可变的。
    }

进入全屏 退出全屏

这种方式使用代理的最佳部分如下:

  • 代理对象继承了原始 Calculator 类的原型,从而。
  • 利用 Proxy 的 set 陷阱来避免修改原始对象。

代码解释:对代码的解释

1). 原型继承机制:

  • 代理不会影响原始的 Calculator 类的原型,
  • 这可以通过检查 proxiedCalculator.prototype 是否等于 Calculator.prototype 并返回 true(为真)来确认。

2). 获取get操作(获取操作):

  • 获取拦截器拦截了代理对象上的属性访问。
  • 我们利用 Reflect.get 安全地访问原始对象的属性及方法。

3). 防止变异:

set 陷阱机制每当尝试修改目标对象的任何属性时抛出错误,这可以确保对象的不可变。

4). 通过代理来使用原型模式:

该代理对象允许访问如addsubtractmultiplydivide这样的方法,这些方法都是在原始对象的原型上定义的。

这里的关键是:

  • 保留原型继承特性: 代理保留了对所有原型方法的访问,使其表现得和原来的计算器一样。
  • 防止篡改内部状态: 设置陷阱确保计算器对象的内部状态不会意外改变。
  • 安全访问属性和方法: 获取陷阱确保只访问有效的属性,提高了健壮性,使系统更可靠。

如果你看到这里,不要忘了点个赞 ❤️,也可以在下面留言区写留言,有任何问题或想法都可以说说。你的反馈对我来说非常重要,我很希望听听你的看法!

點擊查看更多內容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優質文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優惠券免費領

立即參與 放棄機會
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號

舉報

0/150
提交
取消