JavaScript设计模式详解:代理模式的虚拟与缓存应用

作者:巡夜人时间:2026-10-23 05:50:12浏览:117

本文深入解析JavaScript中的代理模式,重点介绍虚拟代理与缓存代理两种常用实现。通过图片预加载和HTTP请求合并的虚拟代理案例,以及计算乘积的缓存代理示例,阐述如何利用代理对象控制访问、优化性能并遵循单一职责原则,帮助开发者在实际业务中灵活应用代理模式。

代理模式概述与单一职责

代理模式提供了一种对程序对象进行受控访问的机制。

引入代理模式的核心目的在于践行面向对象设计中的单一职责原则。

单一职责原则要求一个类(在JavaScript中通常对应对象或函数)仅有一个引起其变化的原因。这有助于提升程序的健壮性和高内聚性,确保当需求发生变化时,对现有设计的意外破坏降至最低。

代理模式包含多种具体形式,如保护代理、远程代理、虚拟代理和缓存代理等。

在JavaScript开发实践中,虚拟代理和缓存代理是应用最为广泛的两种代理类型。

虚拟代理的应用场景

虚拟代理

理解虚拟代理时,可将其视为一个经纪人角色。客户端程序需通过该虚拟代理来间接调用本体对象的方法,而非直接访问本体。

虚拟代理的一个典型应用场景是图片加载前的预处理,即图片预加载功能。


//通过虚拟代理实现图片预加载
//代理模式进行图片预加载的实现思路是: 通过代理对象获取实际显示图片地址并进行加载,同时先让本体对象显示预加载图片,待代理对象将实际图片地址加载完毕后传递给本体对象进行显示即可。

//本体对象
var myImage = (function(){
  var imgNode = new Image()
  document.body.appendChild(imgNode)

  return {
    setSrc: function(src){
      imgNode.src = src
    }
  }
})()

//代理对象
var proxyImage = (function(){
  var img = new Image();     //1、代理对象新建一个img对象
  img.onload = function(){    //4、代理对象img加载真实图片src完成后将src传递给本体对象显示
    myImage.setSrc(this.src)
  }
  return {
    setProxySrc: function(src){
      myImage.setSrc('../images/loding.gif') //2、代理对象控制本体对象使用加载图片src
      img.src = src          //3、代理对象的img对象获取将要传递给本体对象的真实图片src
    }
  }
})()

//通过代理对象来对本体对象进行访问
proxyImage.setProxySrc('https://p1.ssl.qhimgs1.com/t0153297036f4471d81.jpg')

虚拟代理示例demo2:合并HTTP请求,减少网络请求资源消耗


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>代理模式 虚拟代理合并HTTP请求</title>
</head>
<body>
  <div>
    <input type="checkbox" id="1" />1
    <input type="checkbox" id="2" />2
    <input type="checkbox" id="3" />3
    <input type="checkbox" id="4" />4
    <input type="checkbox" id="5" />5
    <input type="checkbox" id="6" />6
    <input type="checkbox" id="7" />7
    <input type="checkbox" id="8" />8
    <input type="checkbox" id="9" />9
  </div>  
</body>
<script>
  //使用

  //本体对象
  var synchornurFile = function(id){
    console.log('开始同步:' + id);
  }

  var proxySynchornurFile = (function(){
    var cache = [],   //集合一段时间内需要同步的id
      timer;   //定时器

    return function(id){
      cache.push(id)

      if(timer){   //保证不会覆盖已经启动的定时器
        return
      }
      timer = setTimeout(function(){
        synchornurFile(cache.join(','))
        clearTimeout(timer)
        timer = null
        cache.length = 0
      }, 2000)
    }
  })()

  var check = document.getElementsByTagName('input')
  for(var i=0; i<check.length; i++){
    check[i].onclick = function(){
      if(this.checked === true){
        proxySynchornurFile(this.id)
      }
    }
  }
</script>
</html>

缓存代理的实现原理

缓存代理

缓存代理的主要作用是为计算开销较大的运算结果提供临时存储。当下次调用时,若传入参数与之前一致,则直接返回已存储的结果,从而避免重复计算。

缓存代理的一个具体实现示例是用于计算两个数值的乘积。


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>代理模式-缓存代理</title>
</head>
<body>
  <input type="text" id="input1">*
  <input type="text" id="input2">
  <div id="result"></div>
  <button type="button" id="btn">计算</button>
</body>
<script>

  //缓存代理示例: 计算乘积
  //本体对象
  var calculate = function(){
    var a = 1;
    for(var i=0; i<arguments.length; i++){
      a = a*arguments[i]
    }
    return a;
  }

  //代理对象,创建缓存代理的工厂,参数fn可以为任意需要进行代理的函数,除了上述计算乘积的本体对象函数外,还可以是计算加减或进行其他操作的本体函数
  var proxyCalculate = function(fn){
    var resultCache = {};

    return function(){
      var args = Array.prototype.join.call(arguments, ',')
      if(args in resultCache){    //测试对象中是否有对应的name,有则直接返回该name的值
        return resultCache[args]
      }
      return resultCache[args] = fn.apply(this, arguments)
    }
  }


  document.getElementById('btn').onclick = function(){
    var v1 = document.getElementById('input1').value
    var v2 = document.getElementById('input2').value
    var result = proxyCalculate(calculate)(v1, v2)

    document.getElementById('result').innerHTML = result
  }

  //总结: 代理模式还有多种,比如保护代理、远程代理等,但js中常用的代理模式有虚拟代理和缓存代理两种。
</script>
</html>

在实际编写业务代码时,无需一开始就强制使用代理模式。建议仅在发现使用代理对象能显著简化逻辑或提升效率时,再引入代理对象。

以上内容是对JavaScript设计模式中代理模式的详细整合,旨在为读者提供实用的技术参考。如有任何疑问,欢迎留言交流,小编将及时回复。感谢大家对本站的支持!

作者最新文章