JavaScript-代理模式

代理模式

使用者無權訪問目標對象
中間加代理,經過代理受權和控制javascript

傳統 UML 類圖

JavaScript 中的代理模式

class ReadImg {
  constructor(fileName) {
    this.fileName = fileName;
    this.loadFromDisk();
  }
  display() {
    console.log("display..." + this.fileName);
  }
  loadFromDisk() {
    console.log("loading..." + this.fileName);
  }
}

class ProxyImg {
  constructor(fileName) {
    this.readImg = new ReadImg(fileName);
  }
  display() {
    this.readImg.display();
  }
}

// test
let proxyImg = new ProxyImg("1.png");
proxyImg.display();

應用場景

網頁代理事件

<!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>Document</title>
  </head>
  <body>
    <div>
      <a href="#">a1</a>
      <a href="#">a2</a>
      <a href="#">a3</a>
      <a href="#">a4</a>
      <a href="#">a5</a>
    </div>

    <script src="https://cdn.bootcss.com/jQuery/3.3.0/jQuery"></script>
    <script>
      var div1 = document.getElementById("div1");
      div1.addEventListener("clink", function(e) {
        var target = e.target;
        if (target.nodeName === "A") {
          alert(target.innerHTML);
        }
      });
    </script>
  </body>
</html>

jQuery

<!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>Document</title>
  </head>
  <body>
    <div>
      <a href="#">a1</a>
      <a href="#">a2</a>
      <a href="#">a3</a>
      <a href="#">a4</a>
      <a href="#">a5</a>
    </div>

    <script src="https://cdn.bootcss.com/jQuery/3.3.0/jQuery"></script>

    <script>
      $("#div1").click(function() {
        // this符合指望
        $(this).addClass("red");
      });
      $("#div1").click(function() {
        setTimeout(function() {
          // this不符合指望
          $(this).addClass("red");
        }, 1000);
      });
    </script>

    <script>
      // 以下方式解決
      $("#div1").click(function() {
        // this符合指望
        $(this).addClass("red");
      });
      $("#div1").click(function() {
        var _this = this;
        setTimeout(function() {
          // this符合指望
          $(this).addClass("red");
        }, 1000);
      });
    </script>
  </body>
</html>

$.proxy

<!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>Document</title>
  </head>
  <body>
    <div>
      <a href="#">a1</a>
      <a href="#">a2</a>
      <a href="#">a3</a>
      <a href="#">a4</a>
      <a href="#">a5</a>
    </div>

    <script src="https://cdn.bootcss.com/jQuery/3.3.0/jQuery"></script>
    <script>
      $("#div1").click(function() {
        var fn = function() {
          $(this).css("background-color", "yellow");
        };
        fn = $.proxy(fn, this);
        setTimeout(fn, 1000);
      });
    </script>

    <script>
      // 由上面那個化簡而來
      $("#div1").click(function() {
        fn = $.proxy(function() {
          $(this).css("background-color", "yellow");
        }, this);
        setTimeout(fn, 1000);
      });
    </script>

    <script>
      // 由上面那個化簡而來
      $("#div1").click(function() {
        setTimeout(
          $.proxy(function() {
            $(this).css("background-color", "yellow");
          }, this),
          1000
        );
      });
    </script>
  </body>
</html>

es6 Proxy

明星和經紀人的關係css

// 明星
let star = {
  name: "張XX",
  age: 25,
  phone: "13910733521"
};

// 經紀人
let agent = new Proxy(star, {
  get: function(target, key) {
    if (key === "phone") {
      // 返回經紀人本身的手機號
      return "18611112222";
    }
    if (key === "price") {
      // 明星不報價,經紀人報價
      return 120000;
    }
    return target[key];
  },
  set: function(target, key, val) {
    if (key === "customPrice") {
      if (val < 100000) {
        // 最低 10w
        throw new Error("價格過低");
      } else {
        target[key] = val;
        return true;
      }
    }
  }
});

// 主辦方
console.log(agent.name);
console.log(agent.age);
console.log(agent.phone);
console.log(agent.price);

// 想本身提供報價(砍價,或者高價爭搶)
agent.customPrice = 150000;
// agent.customPrice = 90000  // 報錯:價格過低
console.log("customPrice", agent.customPrice);

設計原則驗證

  • 代理類和目標類分離,隔離開目標類和使用者
  • 符合開放封閉原則

代理模式 VS 適配器模式

  • 適配器模式:提供一個不一樣的接口(如不一樣版本的插頭)
  • 代理模式:提供如出一轍的接口

代理模式 VS 裝飾器模式

  • 裝飾器模式:拓展功能,原有功能不變且可直接使用
  • 代理模式:顯示原有功能,但通過限制或閹割以後的
相關文章
相關標籤/搜索