5/13 什么是单例模式、观察者模式?window.location怎么用?ajax初识

126 阅读7分钟

什么是设计模式?

以下是GPT的解释

设计模式是一种用于解决软件设计方面常见问题的经验性、可重用的解决方案。它是通过经验总结和抽象提炼而形成的一些有用的设计思想,帮助软件工程师高效地开发出符合软件设计原则的可维护和可扩展的软件系统。设计模式并不是一种具体的代码实现,而是一种抽象的思想和方法论,它可以应用于不同编程语言和技术中。目前比较常用的设计模式有23种,包括工厂模式、单例模式、观察者模式、适配器模式等等。通过使用设计模式,可以提高软件系统的质量、可重用性、可维护性和可扩展性。

image.png

我的理解是:设计模式就是游戏攻略,每当你遇到特定敌人的时候,就可以参考特定的设计模式来应对,设计模式是前人总结的好东西。

现阶段是在学习闭包,所以顺便引入几个设计模式的例子,所以只是简单了解,并不深入。

单例模式

知识点:利用闭包+自执行函数,实现变量res不会被回收

这里的single其实就是我们return的那个匿名函数,是故意这样写的。目的是留存res的值,使得下次调用的时候可以进行判断。

const single = (function () {
    class People {
        constructor(name) {
          this.name = name;
        }
    }
    let res = null;
    return function (name) {
    if (!res) {
      res = new People(name);
    }
    return res;
    };
})();

let xm = single("小明");
console.log(single); 
//很有意思,咱们single就是return的函数本身,然后这个函数又处于闭包,可以使用外部的属性和方法
let xh = single("小红");
console.log(xm);//返回小明
console.log(xh);//还是返回小明
console.log(xm === xh);//true
xh.name = "小红";

console.log(xm);//小红
console.log(xh);//小红

还有更简单的写法:

image.png

实现效果:第一次利用single可以正常实例化对象,如

let xm=new Single("小明");

而后再多次new Single均会得到我们第1次创造的对象

DEMO:生成唯一的登录界面(来自kerwin)

image.png

利用Modal函数生成登录框,第一次点击按钮会生成登录框,后续在其他的地方再度生成的话,都只会返回第一次生成的框,避免产生重复的内容。

观察者模式

我的理解是:这里的观察者模式,其实就是页面多个部分监听了某个数据,一旦数据发生变化,页面也会产生相应的变化。

image.png

所以这里很自然引入一个demo,我们在页面根据原始数据data在页面多处显示内容,而后一旦data变动,其余各处也会发生相应变化。

DEMO 观察者

function observer(target) {
      // 利用代理来夹带私货,
      // 这里的私货就是render()函数
      // 可是在被赋值的时候重新渲染页面
      let dl = new Proxy(target, {
        set(ele, key, value) {
          Reflect.set(ele, key, value);
          // 重新渲染页面  -- render
          render();
        },
        get(ele, key) {
          return Reflect.get(ele, key);
        },
      })

      //接下来开始补全render函数
      function render() {
        //这里并不需要传入参数,直接用target即可
        let myContainer = document.querySelector(".container");
        let str = "";
        //重要任务是拿到data的数据,放入str
        //Object.keys()可以拿到data对象的属性集合成为数组
        // 然后遍历这个数组,取出属性:属性值放进P标签

        Object.keys(target).forEach(item => {
          str += `<p>${item}:${target[item]}</p>`
        });

        myContainer.innerHTML = str;
      }
      render();

      // 返回代理对象
      return dl;
}

let data = {
  info: "孙悟空",
  weapon: "金箍棒",
  age: 600,
};
let copy = observer(data);
copy.info = "齐天大圣";
copy.weapon = "定海神针";
copy.age = 1000;
copy.height = "170cm";

location

这次还捎带讲了location,首先location是啥?

window.location是 JavaScript 中一个用于操作和获取 URL 相关信息的对象,它包含了当前浏览器窗口中当前文档的 URL 信息,包括 URL 的协议、主机名、端口号、路径、查询参数和哈希。该对象有很多有用的属性和方法,如下所示:

  • window.location.href:返回当前 URL 的完整地址。
  • window.location.protocol:返回当前 URL 的协议(http、https等)。
  • window.location.host:返回当前 URL 的主机名和端口号。
  • window.location.hostname:返回当前 URL 的主机名。
  • window.location.port:返回当前 URL 的端口号。
  • window.location.pathname:返回当前 URL 的路径。
  • window.location.search:返回当前 URL 的查询字符串部分。
  • window.location.hash:返回当前 URL 的哈希值。

利用这些知识,咱们可以引入模拟手机底部导航栏的切换。

核心思路就是:配置一个对象数组存储不同的数据,然后我们根据当前页面的location.hash情况进行匹配和修改页面。这里会用到window.onhashchange()来监听

demo初版:

let main = document.querySelector("main");

        let noticeEle = "<p>notice</p>";
        let contactEle = "<p>contact</p>";
        let findEle = "<p>find</p>";
        let myEle = "<p>my</p>";

        //路由配置项
        const routes = [
          {
            path: "#notice",
            component: noticeEle,
          },
          {
            path: "#contact",
            component: contactEle,
          },
          {
            path: "#find",
            component: findEle,
          },
          {
            path: "#my",
            component: myEle,
          },
        ];

        const show = function () {
          let { hash } = location;
          let target = routes.find((item) => item.path == hash);

          main.innerHTML = target.component;
        };

        window.onhashchange = show;
        show();
      };
    </script>
  </head>
  <body>
    <header></header>
    <main></main>
    <footer>
      <a href="#notice">消息</a>
      <a href="#contact">通讯录</a>
      <a href="#find">发现</a>
      <a href="#my">我的</a>
    </footer>
  </body>

demo正则版本

// window.location.hash = "";
      let main = document.querySelector("main");
      //z
      let noticeEle = "<p>notice</p>";
      let contactEle = "<p>contact</p>";
      let findEle = "<p>find</p>";
      let myEle = "<p>my</p>";
      let notFoundEle = "<p>404</p>";

      //路由配置项
      const routes = [
        {
          path: /^$/,
          component: noticeEle,
          //这里配置的hash为空的情况,默认显示消息页面
        },
        {
          path: /^#notice$/,
          component: noticeEle,
        },
        {
          path: /^#contact$/,
          component: contactEle,
        },
        {
          path: /^#find$/,
          component: findEle,
        },
        {
          path: /^#my$/,
          component: myEle,
        },
        {
          path: /^#\w+$/,
          component: notFoundEle,
          //当上述都不匹配,这条就会被选中
        },
      ];

      const show = function () {
        let { hash } = location;
        // let hash = location.hash;
        //解构赋值,用上面的写法也是一样的
        console.log(hash);
        let target = routes.find((item) => item.path.test(hash));
        //这里运用了数组find方法,可以返回第一个符合条件的数组项
        //而find里面有个回调函数,用于规定返回的要求,这里的要求是
        //遍历数组,返回第一个能够通过正则检验的数组项

        main.innerHTML = target.component;
        //将拿到的数据,赋值给我们的页面main
      };

      window.onhashchange = show;
      //绑定事件,每当hash改变就会触发
      show();
      //初始化一下
    };
  </script>
</head>
<body>
  <header></header>
  <main></main>
  <footer>
    <a href="#notice">消息</a>
    <a href="#contact">通讯录</a>
    <a href="#find">发现</a>
    <a href="#my">我的</a>
  </footer>
</body>

效果预览:

screenshots.gif 第1条正则规则中,将消息设置为了默认显示的页面;

第-1条正则规则中,将404设置成了你乱输入的页面(^o^)/~。

ajax初识

XMLHttpRequest 是一种用于发送 HTTP 或 HTTPS 请求的 JavaScript API,它是一种基于浏览器的异步通信技术。XMLHttpRequest 可以在不重新加载整个网页的情况下,更新部分网页的数据。该技术最初被设计用于 AJAX(Asynchronous JavaScript and XML)应用程序,它可以与服务器交换数据而不重新加载整个页面。

使用 XMLHttpRequest 可以通过 JavaScript 脚本发送 HTTP 请求,然后在接收到服务器响应后,进行相应的处理。通过发送异步请求,可以让页面保持响应并进行平滑的用户体验,而不会因为等待服务器响应而阻塞住页面,造成卡顿或者无响应。

XMLHttpRequest 有以下几个主要的 API:

  1. onreadystatechange:用于制定当 readyState 属性发生变化时调用的事件处理程序。
  2. open():用于指定请求的类型、URL、是否使用异步请求等信息。
  3. send():向服务器发送请求。
  4. setRequestHeader():用于设置 HTTP 请求头的内容。
  5. readyState:XMLHttpRequest 中定义的一个属性,表示请求的当前状态。
  6. status:在请求的响应结果返回后,表示 HTTP 状态码。
  7. responseTextresponseXML:分别表示服务器返回的文本和 XML 数据。

XMLHttpRequest 在现代 Web 应用中得到了广泛的应用,它使得在处理 Web 数据交互中能够实现实时性和速度。与此同时,XMLHttpRequest 也提供了丰富的开发接口和多种配置选项,可以满足不同场景下对数据交互过程的需求。

demo

 // 创建一个 XMLHttpRequest 对象
let xhr = new XMLHttpRequest();
// 输出 XMLHttpRequest 对象的 readyState 值
console.log(xhr.readyState);
// 设置请求方式和请求地址
xhr.open("get", "./10-json.json");
// 发送请求
xhr.send();
// 输出 XMLHttpRequest 对象的 readyState 值
console.log(xhr.readyState);

// 监听 XMLHttpRequest 对象的状态变化
xhr.onreadystatechange = function () {
  // 输出 XMLHttpRequest 对象的 readyState 值
  console.log(xhr.readyState);
  // 当 readyState 值为 4 (即请求已完成)时,输出从服务端返回的数据(json格式)
  if (xhr.readyState === 4) {
    console.log(JSON.parse(this.responseText));
  }
};

这段代码的功能就是使用 XMLHttpRequest 对象,向服务器发起一个 GET 请求,获取一个 JSON 格式的数据,并将其输出到控制台。

image.png