定义

一些类是受保护的,类上的方法不能直接被访问,需要设置一层代理,让中间的代理对象增加一些逻辑判断、调用前后执行一些操作,从而实现扩展目标的功能。

类图

Snipaste_2020-09-16_20-59-59.png

代码示例

明星经纪人代理

  1. abstract class Star {
  2. abstract answerPhone(): void;
  3. }
  4. class YaoMing extends Star {
  5. available: boolean = true;
  6. answerPhone(): void {
  7. console.log("我是明星本人");
  8. }
  9. }
  10. class ProxyYaoMing extends Star {
  11. protected yao;
  12. constructor() {
  13. super();
  14. this.yao = new YaoMing();
  15. }
  16. answerPhone(): void {
  17. console.log("你好,我是明星的经纪人");
  18. if (this.yao.available) {
  19. this.yao.answerPhone();
  20. }
  21. }
  22. }
  23. let ym = new ProxyYaoMing();
  24. ym.answerPhone();

图片loading代理的示例

  1. <style>
  2. .bg-container {
  3. width: 860px;
  4. height: 560px;
  5. }
  6. .bg-container img {
  7. width: 100%;
  8. height: 100%;
  9. }
  10. </style>
  11. <body>
  12. <div id="button-wrapper">
  13. <button data-src="image/1.png">image1</button>
  14. <button data-src="image/2.png">image2</button>
  15. <button data-src="image/3.png">image3</button>
  16. </div>
  17. <div class="bg-container">
  18. <img src="image/1.png" id="bg-image" alt="">
  19. </div>
  20. <script>
  21. let btn = document.getElementById("button-wrapper")
  22. class bgImage {
  23. constructor() {
  24. this.imageSrc = document.getElementById("bg-image")
  25. }
  26. setSrc(src) {
  27. this.imageSrc.src = src
  28. }
  29. }
  30. class LoadingBackgroundImage {
  31. static Loading_url = "./image/loading.gif"
  32. constructor() {
  33. this.loadingImage = new bgImage()
  34. }
  35. setSrc(src) {
  36. this.loadingImage.setSrc(LoadingBackgroundImage.Loading_url)
  37. let img = new Image()
  38. img.onload = () => {
  39. this.loadingImage.setSrc(src)
  40. }
  41. img.src = src
  42. }
  43. }
  44. let bgimage = new bgImage()
  45. function setSrc(src) {
  46. let imageSrc = document.getElementById("bg-image")
  47. imageSrc.src = src
  48. }
  49. let loadingimage = new LoadingBackgroundImage()
  50. btn.addEventListener("click", (event) => {
  51. let src = event.target.dataset.src
  52. // console.log(src);
  53. // bgimage.setSrc(src)
  54. loadingimage.setSrc(src)
  55. })
  56. </script>
  57. </body>

图片懒加载代理的示例

  1. <style>
  2. .image {
  3. width: 400px;
  4. height: 320px;
  5. background-color: #ccc;
  6. }
  7. .image img {
  8. width: 100%;
  9. height: 100%;
  10. }
  11. </style>
  12. <body>
  13. <div class="container">
  14. <div class="image">
  15. <img data-src="/image/1.png">
  16. </div>
  17. <div class="image">
  18. <img data-src="/image/2.png">
  19. </div>
  20. <div class="image">
  21. <img data-src="/image/3.png">
  22. </div>
  23. <div class="image">
  24. <img data-src="/image/4.png">
  25. </div>
  26. <div class="image">
  27. <img data-src="/image/5.png">
  28. </div>
  29. <div class="image">
  30. <img data-src="/image/6.png">
  31. </div>
  32. <div class="image">
  33. <img data-src="/image/7.png">
  34. </div>
  35. <div class="image">
  36. <img data-src="/image/8.png">
  37. </div>
  38. <div class="image">
  39. <img data-src="/image/9.png">
  40. </div>
  41. </div>
  42. <script>
  43. let images = document.getElementsByTagName("img")
  44. let clientHeight = window.innerHeight || document.documentElement.clientHeight
  45. function lazyLoad() {
  46. for (let i = 0; i < images.length; i++) {
  47. if (images[i].getBoundingClientRect().top < clientHeight) {
  48. images[i].src = images[i].dataset.src
  49. }
  50. }
  51. }
  52. lazyLoad()
  53. window.addEventListener("scroll", lazyLoad)
  54. </script>
  55. </body>

两个图片代理使用的服务

  1. const express = require("express");
  2. const app = express();
  3. let path = require("path");
  4. app.get("/image/loading.gif", (req, res) => {
  5. res.sendFile(path.join(__dirname, "image", "loading.gif"));
  6. });
  7. app.get("/image/:name", (req, res) => {
  8. // console.log(req.path);
  9. setTimeout(() => {
  10. res.sendFile(path.join(__dirname, req.path));
  11. }, 2000);
  12. });
  13. app.get("/lazy", (req, res) => {
  14. res.sendFile(path.join(__dirname, "lazy.html"));
  15. });
  16. app.get("/", (req, res) => {
  17. res.sendFile(path.join(__dirname, "loading.html"));
  18. });
  19. app.listen(8000);

复杂计算的代理过程

使用缓存进行代理,减少重复计算

  1. function factorial(num) {
  2. if (num == 1) {
  3. console.log("新计算的值...");
  4. return 1;
  5. } else {
  6. return num * factorial(num - 1);
  7. }
  8. }
  9. const proxy = function (fn) {
  10. const cache = {};
  11. return function (num) {
  12. if (num in cache) {
  13. return cache[num];
  14. }
  15. return (cache[num] = fn(num));
  16. };
  17. };
  18. let proxyFactorial = proxy(factorial);
  19. // console.log(factorial(5));
  20. // console.log(factorial(5));
  21. // console.log(factorial(5));
  22. console.log(proxyFactorial(5));
  23. console.log(proxyFactorial(5));
  24. console.log(proxyFactorial(5));

防抖节流的代理过程

防抖节流的定义

  • 防抖:会开启多个定时器,在固定时间内,有新的事件进来就会销毁之前的定时器,然后重新创建一个定时器。
  • 节流:只开启一个定时器,是在一个固定时间段执行事件,在此事件段内可多次执行,过了时间段就不执行。

防抖类似于现实中的黑车,来一个乘客开始倒计时5分钟后发车,又来一个乘客,司机重新计时,再次等一个5分钟。直到等5分钟周期内,都没有新乘客上车,才开始发车。
节流类似于现实中的出租车,来了一个乘客,司机问了目的,说等我喝口水一分钟后立马走,这个一分钟就是固定的定时器,不会再次开启新的定时器。

节流代码示例

  1. <style>
  2. #container {
  3. width: 200px;
  4. height: 600px;
  5. overflow: scroll;
  6. border: 1px solid steelblue;
  7. }
  8. #container .content {
  9. height: 4000px;
  10. }
  11. </style>
  12. <body>
  13. <div id="container">
  14. <p>节流的实现</p>
  15. <div class="content"></div>
  16. </div>
  17. <script>
  18. let container = document.getElementById("container")
  19. let lastTime = Date.now()
  20. function throttle(callback, interval) {
  21. let lastExecuteTime;
  22. return function () {
  23. let content = this
  24. let args = Array.from(arguments)
  25. let now = Date.now()
  26. if (lastExecuteTime) {
  27. if (now - lastExecuteTime >= interval) {
  28. callback.apply(this, args)
  29. lastExecuteTime = now
  30. }
  31. } else {
  32. callback.apply(this, args)
  33. lastExecuteTime = now
  34. }
  35. }
  36. }
  37. const scrollEvent = (event) => {
  38. let nowDate = Date.now()
  39. console.log("触发了滚动事件", (nowDate - lastTime) / 1000);
  40. lastTime = nowDate
  41. }
  42. container.addEventListener("scroll", throttle(scrollEvent, 500))
  43. </script>
  44. </body>

滚动事件会间隔0.5秒执行一次

防抖的示例

  1. <style>
  2. #container {
  3. width: 200px;
  4. height: 600px;
  5. overflow: scroll;
  6. border: 1px solid steelblue;
  7. }
  8. #container .content {
  9. height: 4000px;
  10. }
  11. </style>
  12. <body>
  13. <div id="container">
  14. <p>防抖的实现</p>
  15. <div class="content"></div>
  16. </div>
  17. <script>
  18. let container = document.getElementById("container")
  19. let lastTime = Date.now()
  20. function debounce(callback, delay) {
  21. let timer;
  22. return function () {
  23. let args = Array.from(arguments)
  24. let now = Date.now()
  25. if (timer) {
  26. clearTimeout(timer)
  27. }
  28. timer = setTimeout(() => {
  29. callback.apply(this, args)
  30. }, delay)
  31. }
  32. }
  33. const scrollEvent = (event) => {
  34. let nowDate = Date.now()
  35. console.log("触发了滚动事件", (nowDate - lastTime) / 1000);
  36. lastTime = nowDate
  37. }
  38. container.addEventListener("scroll", debounce(scrollEvent, 500))
  39. </script>
  40. </body>

如果一直滚动,滚动事件不会执行,只有在停止滚动了超过间隔事件0.5秒后才会执行滚动事件。

服务器的反向代理

创建一个端口为8888的服务器,代理到9999

首先创建一个8888服务器

  1. let http = require("http");
  2. let httpProxy = require("http-proxy");
  3. const proxy = httpProxy.createProxyServer();
  4. const server = http.createServer((req, res) => {
  5. proxy.web(req, res, {
  6. target: "http://localhost:9999",
  7. });
  8. });
  9. server.listen(8888, () => console.log(8888));

创建一个9999服务器

  1. let http = require("http");
  2. const server = http.createServer((req, res) => {
  3. res.write("9999");
  4. res.end();
  5. });
  6. server.listen(9999, () => console.log(9999));

访问localhost:8888服务器,页面内容显示9999。此为服务端反向代理的一种形式

代理模式和适配器模式、装饰器模式的区别

  • 代理模式和适配器模式对比,适配器模式提供不同接口,代理模式提供一模一样的接口
  • 代理模式和装饰器模式,装饰器模式原来的功能不变,还可以使用。代理模式改变了原来的功能。