JavaScript全解析——设计模式

JavaScript全解析——设计模式为了解决一类问题给出的 简洁而优化 的解决方案 设计模式不是规则 是程序员开发过程中的经验总结单例模式 一个构造函数一生只有一个 实例对象 script function Person name t script

大家好,欢迎来到IT知识分享网。

●为了解决一类问题给出的 简洁而优化 的解决方案
●设计模式不是规则,是程序员开发过程中的经验总结

单例模式
●一个构造函数一生只有一个 实例对象

<script> function Person(name) { this.name = name } // 核心代码 let instance = null function singleton() { if (!instance) instance = new Person('Jack') return instance } // 实现单例模式创建实例对象 // 第一次调用 singleton() 函数, 此时 instance 就是 null // if 条件为 true, 给 instance 赋值 // 赋值为一个 Person 的实例 // return instance, 把 instance 保存的值赋值给了 p1 // p1 拿到的是 Person 的实例 const p1 = singleton() // 第二次调用 singleton() 函数, 此时 instance 是Perosn 的实例 // if 条件为 false, 后面的代码不执行 // 直接执行 return instance // 又一次把 Person 的实例返回了 // 赋值给了 p2 变量 // p2 拿到的还是 Person 之前的哪一个实例 const p2 = singleton() console.log(p1, p2) console.log(p1 === p2) </script>

单例模式变形
●利用闭包和自执行函数结合的方式实现

function Person(name) { this.name = name } let instance = null function singleton() { if (!instance) instance = new Person('Jack') return instance } // 我们要以闭包的形式得到singleton 不让都暴露在全局 // 这里我们要使用的就是自执行函数 const singleton = (function outer() { function Person(name) { this.name = name } let instance = null return function inner() { if (!instance) instance = new Person('Jack') return instance } })() // const p1 = singleton() // const p2 = singleton() // console.log(p1, p2); // console.log(p1 === p2); // 到了这里我们该放进去的都放进去了接下来我们继续变形 /* Person这个变量名现在只在函数里面有作用 如果我现在把singleton变成Person 和里面的Person有没有影响? 是没有影响的 我们先不改变 为了好说 现在要弄明白一个问题 就是外面的singleton()调用的是那个函数 */ const singleton = (function outer() { function Person(name) { this.name = name } // 在Person的原型对象上定义一个方法 用来修改Person构造函数里面的name的值 Person.prototype.setName = function (val) { this.name = val } // 单例的核心代码 let instance = null return function inner(name) { if (!instance) instance = new Person() // 这里调用setName函数 instance.setName(name) // 这个位置的instance就是person的实例 return instance } })() // const p1 = singleton('Rose') console.log(p1); // const p2 = singleton('Jack') console.log(p2); console.log(p1 === p2); // 再次修改变量 const Person = (function outer() { function Person(name) { this.name = name } // 在Person的原型对象上定义一个方法 用来修改Person构造函数里面的name的值 Person.prototype.setName = function (val) { this.name = val } // 单例的核心代码 let instance = null return function inner(name) { if (!instance) instance = new Person() // 这里调用setName函数 instance.setName(name) // 这个位置的instance就是person的实例 return instance } })() // 所以这个时候我们写new也没有关系 // 因为return的是一个复杂数据类型 自动创建的能力没有了 const p1 = new Person('Rose') console.log(p1); const p2 = new Person('Jack') console.log(p2); console.log(p1 === p2);
<!-- 整个弹出层 --> <div class="dialog"> <!-- 顶部内容 --> <div class="top"> <p>提示</p> <span>X</span> </div> <!-- 中间内容区域 --> <div class="content"> 真实内容 </div> <!-- 底部区域 --> <div class="bottom"> <button>确定</button> </div> </div>

样式

.dialog { width: 600px; height: 360px; border-top: 1px solid #ccc; border-left: 1px solid #ccc; box-shadow: 1px 2px 2px 0px #ccc; background-color: #fff; position: fixed; top: 0; left: 0; right: 0; bottom: 0; margin: auto; display: flex; flex-direction: column; border-radius: 15px; } .dialog > .top { height: 45px; background-color: skyblue; display: flex; box-sizing: border-box; justify-content: space-between; padding: 0 20px; align-items: center; border-bottom: 1px solid #ccc; border-radius: 15px 15px 0 0; } .dialog > .top > p { font-size: 22px; font-weight: 700; color: #fff; } .dialog > .top > span { cursor: pointer; } .dialog > .bottom { height: 45px; display: flex; align-items: center; justify-content: center; border-top: 1px solid #ccc; } .dialog > .bottom > button { font-size: 20px; padding: 0 10px; cursor: pointer; } .dialog > .content { flex: 1; display: flex; justify-content: center; align-items: center; font-size: 20px; }

交互

// 书写单例模式代码 const Dialog = (function () { // 构造函数体 class Dialog { constructor () { this.dialog = document.createElement('div') // 顶部面板 this.top = null // 面板内的文本 this.desc = null // 中间的内容区域 this.content = null // 底部 this.bottom = null // 面本内的文本 this.descText = '提示' // 提示文本内容 this.title = '你好 世界' // 颜色和文本你对照表 this.list = [ { name: 'success', descText: '成功', bgColor: 'green' }, { name: 'danger', descText: '危险', bgColor: 'red' }, { name: 'warning', descText: '警告', bgColor: 'orange' }, { name: 'default', descText: '提示', bgColor: '#fff' }, { name: 'info', descText: '信息', bgColor: 'skyblue' } ] this.single() } // 只执行一次 single () { this.creHTML() this.setCss() this.bindEvent() } // 创建整体结构 creHTML () { // 创建 top 结构 this.top = document.createElement('div') this.desc = document.createElement('p') this.desc.innerText = this.descText this.span = document.createElement('span') this.span.innerText = 'X' // 把 p 和 span 插入到 top 内 this.top.appendChild(this.desc) this.top.appendChild(this.span) // 创建内容结构 this.content = document.createElement('div') this.content.innerText = this.title // 创建底部结构 this.bottom = document.createElement('div') this.btn = document.createElement('button') this.btn.innerText = '确定' this.bottom.appendChild(this.btn) // 把创建好的结构插入到 dialog 内 this.dialog.appendChild(this.top) this.dialog.appendChild(this.content) this.dialog.appendChild(this.bottom) // 把创建好的 dialog 结构插入到 页面内 document.body.appendChild(this.dialog) } // 设置 css 样式 setCss () { // 给 dialog 设置样式 setStyles(this.dialog, { width: '600px', height: '360px', 'border-top': '1px solid #ccc', 'border-left': '1px solid #ccc', 'box-shadow': '1px 2px 2px 0px #ccc', 'background-color': '#fff', position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, margin: 'auto', display: 'flex', 'flex-direction': 'column', 'border-radius': '15px', }) // 给 top 设置样式 setStyles(this.top, { height: '45px', 'background-color': 'skyblue', display: 'flex', 'box-sizing': 'border-box', 'justify-content': 'space-between', padding: '0 20px', 'align-items': 'center', 'border-bottom': '1px solid #ccc', 'border-radius': '15px 15px 0 0' }) // 给 content 设置样式 setStyles(this.content, { flex: 1, display: 'flex', 'justify-content': 'center', 'align-items': 'center', 'font-size': '20px' }) // 给 bottom 设置样式 setStyles(this.bottom, { height: '45px', display: 'flex', 'align-items': 'center', 'justify-content': 'center', 'border-top': '1px solid #ccc' }) // 给 面板提示 设置样式 setStyles(this.desc, { 'font-size': '22px', 'font-weight': 700 }) // 给 面板的 关闭按钮 setStyles(this.span, { cursor: 'pointer' }) // 底部按钮 setStyles(this.btn, { 'font-size': '20px', padding: '0 10px', cursor: 'pointer' }) } // 事件绑定 bindEvent () { this.btn.addEventListener('click', () => { this.dialog.style.display = 'none' }) this.span.addEventListener('click', () => { this.dialog.style.display = 'none' }) } // 每次都要执行 init (title = '', type = 'default') { // 根据你的 文本 和 关键字 设置内容 // 1. 根据类型获取到对照表内的对应信息 const info = this.list.find(item => item.name === type) || { name: 'default', descText: '提示', bgColor: '#fff' } // 2. 根据 info 的内容开始设置 this.top.style.backgroundColor = info.bgColor this.desc.innerText = info.descText this.descText = info.descText this.content.innerText = title this.title = title // 让 dialog 显示出来 this.dialog.style.display = 'flex' } } // 功能函数 function setStyles(ele, styles) { for (let k in styles) { ele.style[k] = styles[k] } } // 单例模式核心代码 let instance = null return function (...arg) { if (!instance) instance = new Dialog() instance.init(...arg) return instance } })()

发布订阅模式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        /*
            发布订阅模式
            例子: 去书店买书
            没有设计模式 就是面向过程
                => 去到书店 , 问有没有书 , 没有就回去了,
                => 过一会儿 , 在去书店问问有没有书 , 没有就回去
                => 过一会儿 , 在去书店问问有没有书 , 没有就回去
                => 过一会儿 , 在去书店问问有没有书 , 没有就回去
                => 过一会儿 , 在去书店问问有没有书 , 没有就回去
                => 过一会儿 , 在去书店问问有没有书 , 没有就回去
                => 过一会儿 , 在去书店问问有没有书 , 没有就回去
                => .....
                => 过一会儿 , 去到书店问问有没有书 , 有了 , 买回去
            发布订阅模式
                => 去到书店 , 问有没有书 , 没有的话留下一个电话号码 , 有了给我打电话
                => 回去等待电话
                => 接到电话了去到书店把书买回去

            发布订阅: 把你想要做的事情都写好(也就是注册到on函数中) , 将来一触发(就是执行了trigger函数)就都做完了
        */

        // 代码实现
        class Observer{
            constructor(status){
                // 表示你的初始状态 , 将来一旦这个状态发生了改变我们要触发
                // 也就是一旦有书了要通知人来买
                this.status = status

                // 定义个消息盒子 , 记录的都有谁和我定了书
                // 定了几本书 , 都有那些书 , 是不是要记录上啊
                this.message = {}
            }

            // 在原型上定义几个方法
            // 这个表示注册一个事件
            // 下面函数调用了传递了实参 , 我们这里要接受形参
            on (type,fn) {
                // type 事件类型
                // fn   事件处理函数
                // 接下来我们要绑定在message内部
                // 我们要判断message内部有没有type这个类型
                // 如果没有我们就让它称为一个数组
                if(!this.message[type]) this.message[type] = []
                // 之后我们向向消息盒子内添加函数
                this.message[type].push(fn)
            }

            // 这个表示取消这个事件
            // 也要接收两个参数
            off (type , fn) {
                // 这个时候我们也要先判断消息盒子中有没有这个类型
                // 如果没有 , 我们就什么都不用做了
                if(!this.message[type]) return
                // 代码能执行到这里说名有这个函数
                // 那我们就解绑这个函数就好了
                // 如何解绑呢?
                this.message[type] = this.message[type].filter(item => item !== fn) // 把不一样的重新赋值 , 那就把一样的筛出去了
            }

            // 触发事件
            trigger (type) {
                // 首先要判断有没有这个事件 , 如果没有就什么都不做了
                if(!this.message[type]) return
                // 代码能执行到这里说明有这个事件
                // 那接下来就触发这个事件就好了
                // 里面就是每一个函数 , 直接调用就可以了
                this.message[type].forEach(item => item())
            }
        }

        // 实例化个对象
        // 这就是一个第三方 , 你可以把这个当做是书店的店员
        const o = new Observer('没有')
        // console.log(o);

        // 准备几个函数
        function handlerA() { console.log('handlerA');}
        function handlerB() { console.log('handlerB');}
        function handlerC() { console.log('handlerC');}

        // 向o 上注册一个事件
        // js这本书来了 , 执行handlerA这个函数
        o.on('js',handlerA)
        o.on('js',handlerB)
        o.on('css',handlerA)
        o.on('css',handlerC)
        o.on('html',handlerB)
        o.on('html',handlerC)
        console.log(o);

        // 向 o 取消一个事件注册
        // 也就是我从别的地方买到这本书了 , 不要在给我打电话了
        o.off('js',handlerA)

        // 都注册好了以后由店员来触发这个事件
        o.trigger('js') // 表示js这本书来了 , 这样就触发了这个函数

    </script>
</body>
</html>
const type = '80%' // '70%' '300-20' '500-50' if (type === '80%') { } else if (type === '70%') { console.log('逻辑实现'); } else if (type === '300-20') { console.log('逻辑实现'); } else if (type === '500-50') { console.log('逻辑实现'); } else if (type === '300-30') { console.log('逻辑实现'); }

策略模式

// 我们利用的还是闭包 const discount = (function () { // 留存一份数据结构 const discountList = { // 这里的数据要如何设计 // 需要有折扣类型和折扣以后的价格 // 标识:能计算出最后的价格(这里的将来需要调用实现 , 也就是这里需要一个函数) '80%':function (total) { return (total * 0.8).toFixed(2) } } // 因为要使用闭包 , 我们需要返回一个函数 function inner(total,type) { // 这里我们出计算方式 // 这里需要根据传递进来的折扣查看discountList中有没有这个折扣就好了 // 需要把 type 当做键来访问 discountList // 如果有返回的是一个函数 , 如果没有返回的是一个undefined // console.log(discountList[type]); // 判断有没有这个折扣 , 没有这个折扣就返回总价 if (!discountList[type]) return total // 代码能执行到这里说明是有这个折扣的 return discountList[type](total) - 0 } // 这里把inner函数当做一个对象来看待 , 向里面插入一些方法 // 添加一个方法 , 专门用来向 discountList内添加折扣类型 inner.add = function (type,fn) { // type:表示添加的折扣 // fn: 是一个函数,用来计算出最后的价格 // console.log(type); // console.log(fn); // 添加到 discountList中 discountList[type] = fn } // 添加一个方法 , 用来删除一种折扣 inner.remove = function (type) { // 删除掉这个折扣 delete discountList[type] } // 我们含可以把这个折扣的列表返回出去查看 inner.getList = function () { return discountList } // 这里返回inner函数 return inner })() // 将来使用 // const res = discount('总价','折扣类型') const res = discount(1000,'80%') console.log(res); // 将来使用添加的折扣 discount.add('300-20', price => price - parseInt(price / 300) * 20) // 添加完毕以后需要在下面计算 const res1 = discount(1000,'300-20') console.log(res1); // 查看折扣列表 const list = discount.getList() console.log(list);

案例-收银台

<!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>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    body {
      padding-left: 200px;
    }

    input, button {
      outline: none;
    }

    ul, li {
      list-style: none;
    }

    .addSale, .calc {
      width: 200px;
      height: 33px;
      background-color: skyblue;
      color: #fff;
      cursor: pointer;
      border: none;
      margin: 30px;
    }

    input {
      width: 300px;
      height: 30px;
      padding-left: 20px;
      font-size: 22px;
      display: block;
      margin: 20px;
    }

    ul {
      display: flex;
      margin: 20px;
    }

    ul > li {
      width: 120px;
      height: 120px;
      background-color: orange;
      margin: 10px;
      display: flex;
      justify-content: center;
      align-items: center;
      color: #fff;
      font-size: 30px;
      cursor: pointer;
    }

    ul > li.active {
      background-color: skyblue;
    }

    p {
      font-size: 100px;
      color: red;
      margin: 20px;
    }

  </style>
</head>
<body>

  <button class="addSale">添加折扣类型</button>

  <!-- 总价格 -->
  <input type="text">

  <!-- 折扣类型 -->
  <ul>
    <li class="active">1111</li>
    <li>1111</li>
    <li>1111</li>
  </ul>

  <button class="calc">计算总价</button>

  <!-- 最终价格 -->
  <p>0.00</p>


  折扣名称(显示在按钮上的文本): <input type="text" class="type">
  计算方式(总价以 x 表示): <input type="text" class="method">

  <script>
    /*
      收银台
    */

    // 0. 获取元素
    const ulBox = document.querySelector('ul')
    const calcBtn = document.querySelector('.calc')
    const totalInp = document.querySelector('input')
    const pBox = document.querySelector('p')

    const addBtn = document.querySelector('.addSale')
    const nameInp = document.querySelector('.type')
    const methodInp = document.querySelector('.method')

    // 0. 以策略模式的形式准备一个 折扣记录
    const calcPrice = (function () {

      // 折扣列表
      const calcList = {
        '80%': function (total) { return (total * 0.8).toFixed(2) },
        '70%': function (total) { return (total * 0.7).toFixed(2) }
      }

      function inner(total, type) {
        if (!calcList[type]) return '0.00'

        return calcList[type](total)
      }

      inner.add = function (type, fn) {
        calcList[type] = fn
      }

      inner.remove = function (type) {
        delete calcList[type]
      }

      inner.getList = function () {
        return calcList
      }

      return inner
    })()

    // 0. 准备变量
    let type = ''

    // 1. 拿到当前所有的折扣渲染 li
    // 将来一旦折扣添加了, 需要重新渲染 li
    bindHtml()
    function bindHtml() {
      // 1-1. 拿到折扣类型列表
      const list = calcPrice.getList()

      // 1-2. 利用 list 渲染 li
      let str = ''
      for (let k in list) {
        str += `
          <li data-type="${ k }">${ k }</li>
        `
      }

      ulBox.innerHTML = str
    }


    // 2. 折扣类型的选择(排他)
    tab()
    function tab() {
      ulBox.addEventListener('click', e => {
        if (e.target.nodeName !== 'LI') return

        // 所有的没有类名
        for (let i = 0; i < ulBox.children.length; i++) {
          ulBox.children[i].classList.remove('active')
        }

        // 当前这个有类名
        e.target.classList.add('active')

        // 记录下当前的折扣类型
        type = e.target.dataset.type
      })
    }

    // 3. 结算按钮的事件
    calcBtn.addEventListener('click', () => {
      // 3-1. 拿到总价
      const totalPrice = totalInp.value - 0

      // 3-2. 计算最终价格
      const resultPrice = calcPrice(totalPrice, type)

      // 3-3. 把最终价渲染
      pBox.innerText = resultPrice
    })

    // 4. 添加折扣
    // 我们只能期望用户给我们两个内容
    //   折扣名称
    //   计算方式
    addBtn.addEventListener('click', () => {

      // 4-1. 拿到折扣名称
      const name = nameInp.value

      // 4-2. 拿到公式
      // 开始组装
      const r = '(parseInt(' + methodInp.value + ')).toFixed(2)'

      // 语法: eval(字符串)
      // 作用: 把该字符串当做js代码来执行
      // const res = eval(r)

      // 开始添加了
      calcPrice.add(name, x => eval(r))

      // 4-3. 从新渲染一遍 li
      bindHtml()

      // 4-4. 把两个文本框清空
      nameInp.value = ''
      methodInp.value = ''
    })
  </script>
</body>
</html>

模块模式
●要是设计模式中的一种
● 模块模式可以指定类想暴露的属性和方法,并且不会污染全局。采用闭包的形式

// 定义个自执行函数 var Person = (function() { // 定义一个变量 var name = 'rose' // 定义一个函数(方法) function sayName() { console.log('我的名字是:',name) } // 返回一个对象 // 因为是自执行函数 , Person得到的就是一个对象 // return { // name: name, // sayName: sayName // } return { name, sayName } })() // 使用 console.log(Person); Person.sayName()

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/124092.html

(0)
上一篇 2025-10-09 14:15
下一篇 2025-10-09 14:26

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信