javaScript设计模式核心原理学习记录(三)

133 阅读2分钟

适配器

概念:实际接口和目标接口,通过适配器转换

典型例子:axios便是通过适配器从而达到适配浏览器和node环境

代理模式

像这种第三方代替我们访问目标对象的模式,就是代理模式。

代理器:Proxy

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截

const target = {}
const proxy = new Proxy(target,handler)

Proxy作为构造函数,有两个参数。

target所要代理的目标对象。

handler配置对象,对每个被代理的操作,提供一个对应的处理函数。

target目标对象,propKey属性名,value属性值,receiver实例本身

保护代理

实现保护代理时,考虑的首要方案就是 ES6 中的 Proxy。

// 规定礼物的数据结构由type和value组成
const present = {
    type: '巧克力',
    value: 60,
}

// 未知妹子
const girl = {
  // 姓名
  name: "小美",
  // 自我介绍
  aboutMe: "...",
  // 年龄
  age: 24,
  // 职业
  career: "teacher",
  // 假头像
  fakeAvatar: "xxxx",
  // 真实头像
  avatar: "xxxx",
  // 手机号
  // 礼物数组
  presents: [],
  // 拒收50块以下的礼物
  bottomValue: 50,
  // 记录最近一次收到的礼物
  lastPresent: present,
};

//普通信息
const baseInfo = ["age", "career"];
//最隐私信息
const privateInfo = ["avatar", "phone"];

//用户
const user = {
  isValidated: true,
  isVIP: false,
};

const JuejinLover = new Proxy(girl, {
  get: function (girl, key) {
    if (baseInfo.indexOf !== -1 && !user.isValidated) {
      alert("您还没有完成验证哦");
      return;
    }

    if (user.isValidated && privateInfo.indexOf(key) && !user.isVIP) {
      alert("只有Vip可以查询信息");
      return;
    }
  },

  set:function(girl,key,val){
      // 最近一次送来的礼物会尝试赋值给lastPresent字段
      if(key === 'lastPresent'){
          if(val.value < girl.bottomValue){
            alert('sorry,您的礼物被拒收了')
            return
          }
      }

      //如果没被拒收
      girl.lastPresent = val
      girl.presents = [...girl.presents,val]
  }
});

由父元素对事件进行处理和分发、间接地将其作用于子元素,因此这种操作从模式上划分属于代理模式。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>事件代理</title>
</head>
<body>
    <div id="father">
        <a href="#">链接1</a>
        <a href="#">链接2</a>
        <a href="#">链接3</a>
        <a href="#">链接4</a>
        <a href="#">链接5</a>
    </div>
</body>
<script>
    const father = document.getElementById('father')
    father.addEventListener('click',function(e){
        if(e.target.tagName==='A'){
            e.preventDefault()
            alert(`我是${e.target.innerText}`)
        }
    })
</script>
</html>

实例,图片预加载:

操作虚拟image,加载好图片后,再赋值给实际图片

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>虚拟代理</title>
    <style>
        .imageContainer{
            width: 300px;
            height: 200px;
        }
        .imageContainer>img{
            width: 100%;
        }
    </style>
</head>
<body>
    <div class="imageContainer">
        <img src="" id="image">
    </div>

</body>
<script>
    class PreLoadImage {
        constructor(imgNode){
            // 获取真实的DOM节点
            this.imgNode = imgNode
        }
        
        //操作img的src属性
        setSrc(imgUrl){
            this.imgNode.src = imgUrl
        }
    }

    class ProxyImage{
        //占位置的url地址
        static LOADIMG_URL = 'https://mnweb.mini1.cn/game/icons/icon1.png'

        constructor(targetImage){
            //目标image,即PreLoadImage实例
            this.targetImage = targetImage;
        }

         // 该方法主要操作虚拟Image,完成加载
        setSrc(targetUrl){
            // 真实img节点初始化时展示的是一个占位图
            this.targetImage.setSrc(ProxyImage.LOADIMG_URL)
             // 创建一个帮我们加载图片的虚拟Image实例
             const virtualImage = new Image()
            // 监听目标图片加载的情况,完成时再将DOM上的真实img节点的src属性设置为目标图片的url
            virtualImage.onload = () => {
                this.targetImage.setSrc(targetUrl)
            }
             // 设置src属性,虚拟Image实例开始加载图片
             virtualImage.src = targetUrl
        }
    }
    
    const imageNode = document.getElementById('image');
    const preLoaadImage = new PreLoadImage(imageNode)
    const imagesExample = new ProxyImage(preLoaadImage)
    imagesExample.setSrc('https://mnweb.mini1.cn/activity/miniweb/snow-activity/img/1-cartoon-content.45002b99.jpg')
</script>
</html>

缓存代理

“用空间换时间”

当我们需要用到某个已经计算过的值的时候,不想再耗时进行二次计算,而是希望能从内存里去取出现成的计算结果。

class bufferProxy {
 // addAll方法会对你传入的所有参数做求和操作
  addAll() {
    console.log("进行了一次计算");
    let result = 0;
    let len = arguments.length;
    for (let i = 0; i < len; i++) {
      result += arguments[i];
    }
    return result;
  }
  // 为求和方法创建代理
  ProxyAdd() {
    let resultBuffer = {};
    // return function () {
      let argumentsStr = Array.prototype.join.call(arguments, ",");
      if (argumentsStr in resultBuffer) {
        return resultBuffer[argumentsStr];
      }
      return resultBuffer[argumentsStr] = this.addAll(...arguments);
    };
//   }
}

const bufferTest = new bufferProxy();
let a = bufferTest.ProxyAdd(1, 2, 3, 5, 8, 1)
console.log(a);

ProxyAdd 针对重复的入参只会计算一次,这将大大节省计算过程中的时间开销**。针对大量入参、做反复计算时,缓存代理的优势将得到更充分的凸显。**