javascript設計模式-代理模式

代理模式是爲一個對象提供一個代用品或佔位符,以便控制對他的訪問。

代理模式是一種非常有意義的模式,在生活中可以找到很多代理的場景。比如,明星都有經紀人作爲代理,明星不會主動與主辦方談論價格和演出的細節,往往是由他的經紀人出面,把商業細節談好之後,再把合同交由給明星簽名。

而我通常使用到代理模式,往往是會在操作一些開銷比較大的運算結果提供暫時的存儲,在下次運算的時候,如果傳遞進來的參數與之前一致,則直接返回前面存儲的運算結果。

我在日常維護工作中,曾經遇到一個場景。在一個後臺管理系統中,表格承擔着非常重要的角色,而在原有的模塊中,是一個分頁的表格,每次點擊分頁的按鈕都會向服務器發起數據請求,但其實在一定的程度上會造成資源的浪費。因爲,我們更加希望,如果用戶已經點擊分頁按鈕的"1",“2”,“3”…的時候,下次再點擊則不會向服務器發起請求。

爲了讓讀者們能更好理解代理模式,下面有幾個簡單的例子:

緩存代理—計算乘積
創建一個用於求乘積的函數

     let mult = function () {
      let a = 1;
      for (let i = 0; i < arguments.length; i++) {
        a = a * arguments[i];
      }
      return a;
    }

加入緩存的代理函數

let createCacheFun = function (fn) {
      let cache = {};
      return function () {
        console.log(fn);
        let arg = Array.prototype.join.call(arguments, ",");
        if (arg in cache) {
          return cache[arg];
        }
        return cache[arg] = fn.apply(this, arguments);
      }
    }
    let a = createCacheFun(mult);
    console.log(a(1, 2, 3, 4)); // 首次計算的結果
    console.log(a(1, 2, 3, 4)); // 緩存後的計算結果

當我們第二次調用createCacheFun (1,2,3,4)的時候,mult 函數並沒有被計算,而是直接返回之前緩存好的計算結果,通過增加緩存代理的方式,mult函數可以繼續專注於自身的職責–計算乘積,緩存的功能是由代理對象實現的。

那麼我們又回到剛剛的場景,利用緩存代理用於ajax異步請求數據。爲了方便起見,使用的技術棧前臺爲bootstrap+jquery,後臺爲node+mysql。
在這裏插入圖片描述

把jquery中的Ajax轉換成Promise的形式。

let baseUrl = "http://localhost:8002"; // 請求地址
    let baseParams = { // 默認展示參數
      currentPage: 1,
      pageSize: 5
    }
    let cache = {} // 緩存的對象

    function Ajax(url, params) {
      params = Object.assign(baseParams, params); // 合併傳遞過來的參數
      if (!cache[params.currentPage]) {
        cache[params.currentPage] = {}
      }
      return new Promise((resolve, reject) => {
        if (Object.keys(cache[params.currentPage]).length > 0) { // 判斷緩存對象時候存在值
          return resolve(cache[params.currentPage])
        } else {
          $.ajax({
            url: baseUrl + url,
            type: "GET",
            data: Object.assign(baseParams, params),
            success: function (data) {
              cache[params.currentPage] = data; // 記錄已經請求後的數據
              resolve(data);
            },
            error: function (data) {
              reject(data);
            }
          })
        }

      })
    }

初次加載和創建節點的方法

// 開始加載
    Ajax("/bookList", baseParams).then(res => {
      let data = res.message
      createdTr(data);
    })
    // 創建節點的方法
    let createdTr = (function () {
      return function (data) {
        $(".table tbody").html("")
        let str = "";
        $.each(data, (i) => {
          str += `
        <tr>
            <td>${data[i].bookName}</td>
            <td>${data[i].bookPrice}</td>
            <td>${data[i].author}</td>
          </tr>
        `
        })
        $(".table tbody").append(str);
      }
    })()

點擊分頁按鈕,數據發生切換。

// 列表點擊
    $(".pagination li").click(function () {
      let num = $(this).find("a").text();
      if (isNaN(num)) {
        return;
      }
      $(this).addClass("active").siblings("li").removeClass("active");
      Ajax("/bookList", {
        currentPage: parseInt(num)
      }).then(res => {
        let data = res.message
        createdTr(data);
      })
    })

在這裏插入圖片描述

利用高階函數動態創建代理

通過傳入高階函數這種更加靈活的方式,把ajax方法作爲一個參數傳入一個專門用於創建緩存代理的工廠函數。

<!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>
  <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.0/css/bootstrap.css" rel="stylesheet">
  <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>

<body>
  <div class="container">
    <div class="panel panel-default">
      <div class="panel-body">
        <table class="table table-striped table-bordered text-center">
          <tr>
            <td>書名</td>
            <td>價格</td>
            <td>作者</td>
          </tr>

        </table>
      </div>
      <div class="panel-footer">
        <nav aria-label="Page navigation">
          <ul class="pagination">
            <li>
              <a href="#" aria-label="Previous">
                <span aria-hidden="true">&laquo;</span>
              </a>
            </li>
            <li class="active"><a href="#">1</a></li>
            <li><a href="#">2</a></li>
            <li><a href="#">3</a></li>
            <li><a href="#">4</a></li>
            <li><a href="#">5</a></li>
            <li>
              <a href="#" aria-label="Next">
                <span aria-hidden="true">&raquo;</span>
              </a>
            </li>
          </ul>
        </nav>
      </div>
    </div>
  </div>
  <script>
    let baseUrl = "http://localhost:8002";
    let baseParams = {
      currentPage: 1,
      pageSize: 5
    }

    function Ajax(url, params) {
      params = Object.assign(baseParams, params);
      return new Promise((resolve, reject) => {
        $.ajax({
          url: baseUrl + url,
          type: "GET",
          data: Object.assign(baseParams, params),
          success: function (data) {
            resolve(data);
          },
          error: function (data) {
            reject(data);
          }
        })
      })
    }
    // 創建節點的方法
    let createdTr = (function () {
      return function (data) {
        $(".table tbody").html("")
        let str = "";
        $.each(data, (i) => {
          str += `
        <tr>
            <td>${data[i].bookName}</td>
            <td>${data[i].bookPrice}</td>
            <td>${data[i].author}</td>
          </tr>
        `
        })
        $(".table tbody").append(str);
      }
    })()
    //  高階函數實現緩存代理
    let createCacheFun = function (fn) {
      let cache = {};
      return function (url, baseParams) {
        // console.log(baseParams);
        if (!cache[baseParams.currentPage]) {
          cache[baseParams.currentPage] = fn.apply(this, arguments);
        }
        return cache[baseParams.currentPage];
      }
    }
    // 開始加載
    let cacheAjax = createCacheFun(Ajax);
    cacheAjax("/bookList", baseParams).then((res) => {
      console.log(res);
      let data = res.message
      createdTr(data);
    })
    // 列表點擊
    $(".pagination li").click(function () {
      let num = $(this).find("a").text();
      if (isNaN(num)) {
        return;
      }
      $(this).addClass("active").siblings("li").removeClass("active");
      cacheAjax("/bookList", {
        currentPage: parseInt(num)
      }).then(res => {
        let data = res.message
        createdTr(data);
      })
    })
  </script>
</body>

</html>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章