整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          html基礎(chǔ)-2

          ody

          常用標(biāo)簽

          body分為塊級和行內(nèi)

          • 塊級 獨(dú)占整行
          • 行內(nèi) 內(nèi)容有多少就占多少

          文本格式化標(biāo)簽

          br 換行

          <div>qwer<br/>zxcv</div>

          div和span

          • div 塊級標(biāo)簽樣式
          <div style="background-color: green;">qwer</div>
          • span 行內(nèi)標(biāo)簽樣式
          span style="background-color: green;">zxcv</span>

          p 段落

          <p>hahahahah</p> 
          <p>hahahahahaaa</p>   

          h 標(biāo)題

          h1~h6y依次變小

             <div>默認(rèn)文字字體</div>
             <h1>再再再再再粗一點(diǎn)</h1>
             <h2>再再再再粗一點(diǎn)</h2>
             <h3>再再再粗一點(diǎn)</h3>
             <h4>再再粗一點(diǎn)</h4>
             <h5>再粗一點(diǎn)</h5>
             <h6>粗一點(diǎn)</h6>
          

          鏈接

          • a 超鏈接,跳轉(zhuǎn)到指定的地址
          <a href="http://www.baidu.com" title="baidu">百度</a>
          • 錨點(diǎn),點(diǎn)擊后跳轉(zhuǎn)到指定位置
          <!DOCTYPE html>
          <html lang="en">
          <head>
             <meta charset="UTF-8">
             <title>野雞平臺</title>
          </head>
          <body>
          <h1>章節(jié)</h1>
          <a href="#i1" title="第一章">第一章 寂寞的春天</a>
          <a href="#i2" title="第二章">第二章 寂寞的夏天</a>
          <a href="#i3" title="第三章">第三章 寂寞的秋天</a>
          <a href="#i4" title="第四章">第四章 寂寞的冬天</a>
          <h1>內(nèi)容</h1>
          <div style="height: 1000px;" id="i1">
             <h3>第一章 寂寞的春天</h3>
             <p>春暖花開,萬物復(fù)蘇,又到了交配的季節(jié)。</p>
          </div>
          <div style="height: 1000px;" id="i2">
             <h3>第二章 寂寞的夏天</h3>
             <p>夏天夏天悄悄過去留下小咪咪</p>
          </div>
          <div style="height: 1000px;" id="i3">
             <h3>第三章 寂寞的秋天</h3>
             <p>今年的秋天真是寂寞呀?。?!</p>
          </div>
          <div style="height: 1000px;" id="i4">
             <h3>第四章 寂寞的冬天</h3>
             <p>下雪</p>
          </div>
          </body>
          </html>
          

          ul ol dl 列表展示

          • ul 無序列表
          • ol 有序列表
          • dl 層級列表
          • li 列表項目
          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>野雞平臺</title>
          </head>
          <body>
              <ul>
                  <li>周杰倫</li>
                  <li>林俊杰</li>
                  <li>王力宏</li>
              </ul>
              <ol>
                  <li>鐵錘</li>
                  <li>鋼彈</li>
                  <li>狗蛋</li>
              </ol>
              <dl>
                  <dt>河北省</dt>
                  <dd>邯鄲</dd>
                  <dd>石家莊</dd>
                  <dt>山西省</dt>
                  <dd>太原</dd>
                  <dd>平遙</dd>
              </dl>
          </body>
          </html>
          

          table 表格

          • table表格標(biāo)題
          • thead表格標(biāo)題
          • tr 行標(biāo)簽
          • th 列名
          • tbody表格內(nèi)容
          • tr 列標(biāo)簽
          • td 列內(nèi)容
          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>6666</title>
          </head>
          <body>
              <table border="3">   <!--border 選擇表格樣式-->>
                  <thead>
                      <tr>
                          <th>姓名</th>
                          <th>年齡</th>
                          <th>愛好</th>
                      </tr>
                  </thead>
                  <tbody>
                      <tr>
                          <td>xxxx</td>
                          <td>18</td>
                          <td>看書</td>
                      </tr>
                      <tr>
                          <td rowspan="3">aaaa</td>  <!--rowspan 合并單元格-->>
                          <td>18</td>
                          <td>吃飯</td>
                      </tr>
                      <tr>
                          <td>33</td>>
                          <td>heiheihei</td>>
                      </tr>>
                  </tbody>
              </table>
          </body>
          </html>
          

          img 圖片

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>6666</title>
          </head>
          <body>
              <!--顯示本地圖片,找不到圖片則顯示alt中的文字-->
              <img src="img/lover.png" alt="美女">
              <!--顯示網(wǎng)絡(luò)圖片-->
              <img src="https://images.cnblogs.com/cnblogs_com/wupeiqi/662608/t_212313579359018.png" alt="妹子">
          </body>
          </html>
          

          用戶交互

          按鈕標(biāo)簽

          type

          • buttom: 普通
          • submit: 提交
          • reset: 重置
           <button type="button"> 按鈕 </button>

          input

          • text,文本框。
          • password,密碼框。
          • radio,單選框(必須設(shè)置name屬性相同,否則無法實(shí)現(xiàn))。
          • checkbox,復(fù)選框。
          • file,文件上傳。
          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>666666</title>
          </head>
          <body>
              <h3>文本框</h3>
              <input type="text">
              <h3>密碼框</h3>
              <input type="password">
              <h3>單選框</h3>
              <input type="radio" name="gender">男
              <input type="radio" name="gender">女
              <h3>復(fù)選框</h3>
              <input type="checkbox">籃球
              <input type="checkbox">足球
              <input type="checkbox">橄欖球
              <h3>上傳文件</h3>
              <input type="file">
          </body>
          </html>

          select下拉框

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>HTML學(xué)習(xí)</title>
          </head>
          <body>
              <h3>單選</h3>
              <select>
                  <option>上海</option>
                  <option>北京</option>
                  <option>深圳</option>
              </select>
              <h3>多選</h3>
              <select multiple>
                  <option>上海</option>
                  <option>北京</option>
                  <option>深圳</option>
              </select>
          </body>
          </html>

          textarea多行文本框

          <!DOCTYPE html>
          臥槽,無情呀
          <html lang="en">
          <head>
              <meta charset="UTF-8" />
              <title>HTML學(xué)習(xí)</title>
          </head>
          <body>
              <textarea>文本內(nèi)容寫在這里...</textarea>
          </body>
          </body>
          </html>

          form表單

          用于提交數(shù)據(jù)到后臺

          • 提交時,只會提交form標(biāo)簽內(nèi)部【用戶交互】相關(guān)的標(biāo)簽。
          • <input type="submit" value="提交"> 用于提交當(dāng)前所在的表單。
          • <input type="reset" value="重置"> 用于重置當(dāng)前標(biāo)簽中的選項。
          • form標(biāo)簽內(nèi)置屬性action="/xx/" ,表示表單要提交的地址。method="get",表示表單的提交方式。enctype="multipart/form-data",如果form內(nèi)部有文件上傳,必須加上此設(shè)置
          • form內(nèi)部【用戶交互】相關(guān)標(biāo)簽必須設(shè)置name,不然提交數(shù)據(jù)后后端無法獲取
          // 提交表單之后,實(shí)際上會將表單中的數(shù)據(jù)構(gòu)造成一種特殊的結(jié)構(gòu),發(fā)送給后臺,類似于:
          {
              user:用戶輸入的姓名,
            pwd:用戶輸入的密碼,
              ...
          }
          
          • radio、checkbox、select 除了要設(shè)置name屬性以外,還必須設(shè)置value屬性,因為這三中標(biāo)簽在form表單提交時,不會把看到的內(nèi)容提交到后臺,而是把選擇選項對應(yīng)的value值提交到后臺。

          <script src="https://lf3-cdn-tos.bytescm.com/obj/cdn-static-resource/tt_player/tt.player.js?v=20160723"></script>

          序列定義:

          文字前面的符號或者序列

          特點(diǎn):

          塊元素 ,獨(dú)立成行,行間距 , 行縮進(jìn)

          類別:

          包括兩種,一種是有序的叫ol,一種是無序的ul

          兩者共同點(diǎn):

          這兩個都同樣嵌套li。

          無序標(biāo)簽屬性 :

          type disc 實(shí)心圓(默認(rèn)屬性) circle空心圓 square實(shí)心四方

          有序標(biāo)簽屬性:

          type =1 a A i I

          start="n"從第n個數(shù)開始計數(shù)

          嵌套關(guān)系,UL可以嵌套UL,原則是繼續(xù)縮進(jìn)

          部分講設(shè)計模式的文章都是使用的Java、C++這樣的以類為基礎(chǔ)的靜態(tài)類型語言,作為前端開發(fā)者,js這門基于原型的動態(tài)語言,函數(shù)成為了一等公民,在實(shí)現(xiàn)一些設(shè)計模式上稍顯不同,甚至簡單到不像使用了設(shè)計模式,有時候也會產(chǎn)生些困惑。
          設(shè)計模式 Design Pattern 是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過分類編目的、代碼設(shè)計經(jīng)驗的總結(jié),使用設(shè)計模式是為了可重用代碼、讓代碼更容易被他人理解并且保證代碼可靠性。

          為什么要學(xué)習(xí)設(shè)計模式:

          • 設(shè)計模式來源眾多專家的經(jīng)驗和智慧,它們是從許多優(yōu)秀的軟件系統(tǒng)中總結(jié)出的成功的、能夠?qū)崿F(xiàn)可維護(hù)性、復(fù)用的設(shè)計方案,使用這些方案將可以讓我們避免做一些重復(fù)性的工作
          • 設(shè)計模式提供了一套通用的設(shè)計詞匯和一種通用的形式來方便開發(fā)人員之間溝通和交流,使得設(shè)計方案更加通俗易懂
          • 大部分設(shè)計模式都兼顧了系統(tǒng)的可重用性和可擴(kuò)展性,這使得我們可以更好地重用一些已有的設(shè)計方案、功能模塊甚至一個完整的軟件系統(tǒng),避免我們經(jīng)常做一些重復(fù)的設(shè)計、編寫一些重復(fù)的代碼
          • 合理使用設(shè)計模式并對設(shè)計模式的使用情況進(jìn)行文檔化,將有助于別人更快地理解系統(tǒng)
          • 學(xué)習(xí)設(shè)計模式將有助于初學(xué)者更加深入地理解面向?qū)ο笏枷?/span>

          JavaScript 中常見設(shè)計模式

          設(shè)計模式分類(23種設(shè)計模式)

          創(chuàng)建型

            • 單例模式
            • 原型模式
            • 工廠模式
            • 抽象工廠模式
            • 建造者模式

          結(jié)構(gòu)型

            • 適配器模式
            • 裝飾器模式
            • 代理模式
            • 外觀模式
            • 橋接模式
            • 組合模式
            • 享元模式

          行為型

            • 觀察者模式
            • 迭代器模式
            • 策略模式
            • 模板方法模式
            • 職責(zé)鏈模式
            • 命令模式
            • 備忘錄模式
            • 狀態(tài)模式
            • 訪問者模式
            • 中介者模式
            • 解釋器模式

          各設(shè)計模式關(guān)鍵詞

          看完了上述設(shè)計模式后,把它們的關(guān)鍵詞特點(diǎn)羅列出來,以后提到某種設(shè)計模式,進(jìn)而聯(lián)想相應(yīng)的關(guān)鍵詞和例子,從而心中有數(shù)。

          工廠模式

          工廠模式定義一個用于創(chuàng)建對象的接口,這個接口由子類決定實(shí)例化哪一個類。該模式使一個類的實(shí)例化延遲到了子類。而子類可以重寫接口方法以便創(chuàng)建的時候指定自己的對象類型。

          class Product {
              constructor(name) {
                  this.name = name
              }
              init() {
                  console.log('init')
              }
              fun() {
                  console.log('fun')
              }
          }
          
          class Factory {
              create(name) {
                  return new Product(name)
              }
          }
          
          // use
          let factory = new Factory()
          let p = factory.create('p1')
          p.init()
          p.fun()
          

          適用場景

          • 如果你不想讓某個子系統(tǒng)與較大的那個對象之間形成強(qiáng)耦合,而是想運(yùn)行時從許多子系統(tǒng)中進(jìn)行挑選的話,那么工廠模式是一個理想的選擇
          • 將new操作簡單封裝,遇到new的時候就應(yīng)該考慮是否用工廠模式;
          • 需要依賴具體環(huán)境創(chuàng)建不同實(shí)例,這些實(shí)例都有相同的行為,這時候我們可以使用工廠模式,簡化實(shí)現(xiàn)的過程,同時也可以減少每種對象所需的代碼量,有利于消除對象間的耦合,提供更大的靈活性

          優(yōu)點(diǎn)

          • 創(chuàng)建對象的過程可能很復(fù)雜,但我們只需要關(guān)心創(chuàng)建結(jié)果。
          • 構(gòu)造函數(shù)和創(chuàng)建者分離, 符合“開閉原則”
          • 一個調(diào)用者想創(chuàng)建一個對象,只要知道其名稱就可以了。
          • 擴(kuò)展性高,如果想增加一個產(chǎn)品,只要擴(kuò)展一個工廠類就可以。

          缺點(diǎn)

          • 添加新產(chǎn)品時,需要編寫新的具體產(chǎn)品類,一定程度上增加了系統(tǒng)的復(fù)雜度
          • 考慮到系統(tǒng)的可擴(kuò)展性,需要引入抽象層,在客戶端代碼中均使用抽象層進(jìn)行定義,增加了系統(tǒng)的抽象性和理解難度

          什么時候不用

          當(dāng)被應(yīng)用到錯誤的問題類型上時,這一模式會給應(yīng)用程序引入大量不必要的復(fù)雜性.除非為創(chuàng)建對象提供一個接口是我們編寫的庫或者框架的一個設(shè)計上目標(biāo),否則我會建議使用明確的構(gòu)造器,以避免不必要的開銷。

          由于對象的創(chuàng)建過程被高效的抽象在一個接口后面的事實(shí),這也會給依賴于這個過程可能會有多復(fù)雜的單元測試帶來問題。

          例子

          • 曾經(jīng)我們熟悉的JQuery的$()就是一個工廠函數(shù),它根據(jù)傳入?yún)?shù)的不同創(chuàng)建元素或者去尋找上下文中的元素,創(chuàng)建成相應(yīng)的jQuery對象
          class jQuery {
              constructor(selector) {
                  super(selector)
              }
              add() {
                  
              }
            // 此處省略若干API
          }
          
          window.$ = function(selector) {
              return new jQuery(selector)
          }
          
          • vue 的異步組件

          在大型應(yīng)用中,我們可能需要將應(yīng)用分割成小一些的代碼塊,并且只在需要的時候才從服務(wù)器加載一個模塊。為了簡化,Vue 允許你以一個工廠函數(shù)的方式定義你的組件,這個工廠函數(shù)會異步解析你的組件定義。Vue 只有在這個組件需要被渲染的時候才會觸發(fā)該工廠函數(shù),且會把結(jié)果緩存起來供未來重渲染。例如:

          Vue.component('async-example', function (resolve, reject) {
            setTimeout(function () {
              // 向 `resolve` 回調(diào)傳遞組件定義
              resolve({
                template: '<div>I am async!</div>'
              })
            }, 1000)
          })
          

          單例模式

          一個類只有一個實(shí)例,并提供一個訪問它的全局訪問點(diǎn)。

           class LoginForm {
              constructor() {
                  this.state = 'hide'
              }
              show() {
                  if (this.state === 'show') {
                      alert('已經(jīng)顯示')
                      return
                  }
                  this.state = 'show'
                  console.log('登錄框顯示成功')
              }
              hide() {
                  if (this.state === 'hide') {
                      alert('已經(jīng)隱藏')
                      return
                  }
                  this.state = 'hide'
                  console.log('登錄框隱藏成功')
              }
           }
           LoginForm.getInstance = (function () {
               let instance
               return function () {
                  if (!instance) {
                      instance = new LoginForm()
                  }
                  return instance
               }
           })()
          
          let obj1 = LoginForm.getInstance()
          obj1.show()
          
          let obj2 = LoginForm.getInstance()
          obj2.hide()
          
          console.log(obj1 === obj2)
          

          優(yōu)點(diǎn)

          • 劃分命名空間,減少全局變量
          • 增強(qiáng)模塊性,把自己的代碼組織在一個全局變量名下,放在單一位置,便于維護(hù)
          • 且只會實(shí)例化一次。簡化了代碼的調(diào)試和維護(hù)

          缺點(diǎn)

          • 由于單例模式提供的是一種單點(diǎn)訪問,所以它有可能導(dǎo)致模塊間的強(qiáng)耦合 從而不利于單元測試。無法單獨(dú)測試一個調(diào)用了來自單例的方法的類,而只能把它與那個單例作為一個單元一起測試。

          場景例子

          • 定義命名空間和實(shí)現(xiàn)分支型方法
          • 登錄框
          • vuex 和 redux中的store

          適配器模式

          將一個類的接口轉(zhuǎn)化為另外一個接口,以滿足用戶需求,使類之間接口不兼容問題通過適配器得以解決。

          class Plug {
            getName() {
              return 'iphone充電頭';
            }
          }
          
          class Target {
            constructor() {
              this.plug = new Plug();
            }
            getName() {
              return this.plug.getName() + ' 適配器Type-c充電頭';
            }
          }
          
          let target = new Target();
          target.getName(); // iphone充電頭 適配器轉(zhuǎn)Type-c充電頭
          

          優(yōu)點(diǎn)

          • 可以讓任何兩個沒有關(guān)聯(lián)的類一起運(yùn)行。
          • 提高了類的復(fù)用。
          • 適配對象,適配庫,適配數(shù)據(jù)

          缺點(diǎn)

          • 額外對象的創(chuàng)建,非直接調(diào)用,存在一定的開銷(且不像代理模式在某些功能點(diǎn)上可實(shí)現(xiàn)性能優(yōu)化)
          • 如果沒必要使用適配器模式的話,可以考慮重構(gòu),如果使用的話,盡量把文檔完善

          場景

          • 整合第三方SDK
          • 封裝舊接口
          // 自己封裝的ajax, 使用方式如下
          ajax({
              url: '/getData',
              type: 'Post',
              dataType: 'json',
              data: {
                  test: 111
              }
          }).done(function() {})
          // 因為歷史原因,代碼中全都是:
          // $.ajax({....})
          
          // 做一層適配器
          var $ = {
              ajax: function (options) {
                  return ajax(options)
              }
          }
          
          • vue的computed
          <template>
              <div id="example">
                  <p>Original message: "{{ message }}"</p>  <!-- Hello -->
                  <p>Computed reversed message: "{{ reversedMessage }}"</p>  <!-- olleH -->
              </div>
          </template>
          <script type='text/javascript'>
              export default {
                  name: 'demo',
                  data() {
                      return {
                          message: 'Hello'
                      }
                  },
                  computed: {
                      reversedMessage: function() {
                          return this.message.split('').reverse().join('')
                      }
                  }
              }
          </script>

          原有data 中的數(shù)據(jù)不滿足當(dāng)前的要求,通過計算屬性的規(guī)則來適配成我們需要的格式,對原有數(shù)據(jù)并沒有改變,只改變了原有數(shù)據(jù)的表現(xiàn)形式

          不同點(diǎn)

          適配器與代理模式相似

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

          裝飾者模式

          • 動態(tài)地給某個對象添加一些額外的職責(zé),,是一種實(shí)現(xiàn)繼承的替代方案
          • 在不改變原對象的基礎(chǔ)上,通過對其進(jìn)行包裝擴(kuò)展,使原有對象可以滿足用戶的更復(fù)雜需求,而不會影響從這個類中派生的其他對象
          class Cellphone {
              create() {
                  console.log('生成一個手機(jī)')
              }
          }
          class Decorator {
              constructor(cellphone) {
                  this.cellphone = cellphone
              }
              create() {
                  this.cellphone.create()
                  this.createShell(cellphone)
              }
              createShell() {
                  console.log('生成手機(jī)殼')
              }
          }
          // 測試代碼
          let cellphone = new Cellphone()
          cellphone.create()
          
          console.log('------------')
          let dec = new Decorator(cellphone)
          dec.create()
          

          場景例子

          • 比如現(xiàn)在有4 種型號的自行車,我們?yōu)槊糠N自行車都定義了一個單

          獨(dú)的類。現(xiàn)在要給每種自行車都裝上前燈、尾 燈和鈴鐺這3 種配件。如果使用繼承的方式來給 每種自行車創(chuàng)建子類,則需要 4×3 = 12 個子類。 但是如果把前燈、尾燈、鈴鐺這些對象動態(tài)組 合到自行車上面,則只需要額外增加3 個類

          • ES7 Decorator 阮一峰
          • core-decorators

          優(yōu)點(diǎn)

          • 裝飾類和被裝飾類都只關(guān)心自身的核心業(yè)務(wù),實(shí)現(xiàn)了解耦。
          • 方便動態(tài)的擴(kuò)展功能,且提供了比繼承更多的靈活性。

          缺點(diǎn)

          • 多層裝飾比較復(fù)雜。
          • 常常會引入許多小對象,看起來比較相似,實(shí)際功能大相徑庭,從而使得我們的應(yīng)用程序架構(gòu)變得復(fù)雜起來

          代理模式

          是為一個對象提供一個代用品或占位符,以便控制對它的訪問

          假設(shè)當(dāng)A 在心情好的時候收到花,小明表白成功的幾率有

          60%,而當(dāng)A 在心情差的時候收到花,小明表白的成功率無限趨近于0。 小明跟A 剛剛認(rèn)識兩天,還無法辨別A 什么時候心情好。如果不合時宜地把花送給A,花 被直接扔掉的可能性很大,這束花可是小明吃了7 天泡面換來的。 但是A 的朋友B 卻很了解A,所以小明只管把花交給B,B 會監(jiān)聽A 的心情變化,然后選 擇A 心情好的時候把花轉(zhuǎn)交給A,代碼如下:

          let Flower = function() {}
          let xiaoming = {
            sendFlower: function(target) {
              let flower = new Flower()
              target.receiveFlower(flower)
            }
          }
          let B = {
            receiveFlower: function(flower) {
              A.listenGoodMood(function() {
                A.receiveFlower(flower)
              })
            }
          }
          let A = {
            receiveFlower: function(flower) {
              console.log('收到花'+ flower)
            },
            listenGoodMood: function(fn) {
              setTimeout(function() {
                fn()
              }, 1000)
            }
          }
          xiaoming.sendFlower(B)
          

          場景

          • HTML元 素事件代理
          <ul id="ul">
            <li>1</li>
            <li>2</li>
            <li>3</li>
          </ul>
          <script>
            let ul = document.querySelector('#ul');
            ul.addEventListener('click', event => {
              console.log(event.target);
            });
          </script>
          • ES6 的 proxy 阮一峰Proxy
          • jQuery.proxy()方法

          優(yōu)點(diǎn)

          • 代理模式能將代理對象與被調(diào)用對象分離,降低了系統(tǒng)的耦合度。代理模式在客戶端和目標(biāo)對象之間起到一個中介作用,這樣可以起到保護(hù)目標(biāo)對象的作用
          • 代理對象可以擴(kuò)展目標(biāo)對象的功能;通過修改代理對象就可以了,符合開閉原則;

          缺點(diǎn)

          處理請求速度可能有差別,非直接訪問存在開銷

          不同點(diǎn)

          裝飾者模式實(shí)現(xiàn)上和代理模式類似

          • 裝飾者模式: 擴(kuò)展功能,原有功能不變且可直接使用
          • 代理模式: 顯示原有功能,但是經(jīng)過限制之后的

          外觀模式

          為子系統(tǒng)的一組接口提供一個一致的界面,定義了一個高層接口,這個接口使子系統(tǒng)更加容易使用

          1. 兼容瀏覽器事件綁定
          let addMyEvent = function (el, ev, fn) {
              if (el.addEventListener) {
                  el.addEventListener(ev, fn, false)
              } else if (el.attachEvent) {
                  el.attachEvent('on' + ev, fn)
              } else {
                  el['on' + ev] = fn
              }
          }; 
          
          1. 封裝接口
          let myEvent = {
              // ...
              stop: e => {
                  e.stopPropagation();
                  e.preventDefault();
              }
          };
          

          場景

          • 設(shè)計初期,應(yīng)該要有意識地將不同的兩個層分離,比如經(jīng)典的三層結(jié)構(gòu),在數(shù)據(jù)訪問層和業(yè)務(wù)邏輯層、業(yè)務(wù)邏輯層和表示層之間建立外觀Facade
          • 在開發(fā)階段,子系統(tǒng)往往因為不斷的重構(gòu)演化而變得越來越復(fù)雜,增加外觀Facade可以提供一個簡單的接口,減少他們之間的依賴。
          • 在維護(hù)一個遺留的大型系統(tǒng)時,可能這個系統(tǒng)已經(jīng)很難維護(hù)了,這時候使用外觀Facade也是非常合適的,為系系統(tǒng)開發(fā)一個外觀Facade類,為設(shè)計粗糙和高度復(fù)雜的遺留代碼提供比較清晰的接口,讓新系統(tǒng)和Facade對象交互,F(xiàn)acade與遺留代碼交互所有的復(fù)雜工作。

          參考: 大話設(shè)計模式

          優(yōu)點(diǎn)

          • 減少系統(tǒng)相互依賴。
          • 提高靈活性。
          • 提高了安全性

          缺點(diǎn)

          • 不符合開閉原則,如果要改東西很麻煩,繼承重寫都不合適。

          觀察者模式

          定義了一種一對多的關(guān)系,讓多個觀察者對象同時監(jiān)聽某一個主題對象,這個主題對象的狀態(tài)發(fā)生變化時就會通知所有的觀察者對象,使它們能夠自動更新自己,當(dāng)一個對象的改變需要同時改變其它對象,并且它不知道具體有多少對象需要改變的時候,就應(yīng)該考慮使用觀察者模式。

          • 發(fā)布 & 訂閱
          • 一對多
          // 主題 保存狀態(tài),狀態(tài)變化之后觸發(fā)所有觀察者對象
          class Subject {
            constructor() {
              this.state = 0
              this.observers = []
            }
            getState() {
              return this.state
            }
            setState(state) {
              this.state = state
              this.notifyAllObservers()
            }
            notifyAllObservers() {
              this.observers.forEach(observer => {
                observer.update()
              })
            }
            attach(observer) {
              this.observers.push(observer)
            }
          }
          
          // 觀察者
          class Observer {
            constructor(name, subject) {
              this.name = name
              this.subject = subject
              this.subject.attach(this)
            }
            update() {
              console.log(`${this.name} update, state: ${this.subject.getState()}`)
            }
          }
          
          // 測試
          let s = new Subject()
          let o1 = new Observer('o1', s)
          let o2 = new Observer('02', s)
          
          s.setState(12)
          

          場景

          • DOM事件
          document.body.addEventListener('click', function() {
              console.log('hello world!');
          });
          document.body.click()
          
          • vue 響應(yīng)式

          優(yōu)點(diǎn)

          • 支持簡單的廣播通信,自動通知所有已經(jīng)訂閱過的對象
          • 目標(biāo)對象與觀察者之間的抽象耦合關(guān)系能單獨(dú)擴(kuò)展以及重用
          • 增加了靈活性
          • 觀察者模式所做的工作就是在解耦,讓耦合的雙方都依賴于抽象,而不是依賴于具體。從而使得各自的變化都不會影響到另一邊的變化。

          缺點(diǎn)

          過度使用會導(dǎo)致對象與對象之間的聯(lián)系弱化,會導(dǎo)致程序難以跟蹤維護(hù)和理解

          狀態(tài)模式

          允許一個對象在其內(nèi)部狀態(tài)改變的時候改變它的行為,對象看起來似乎修改了它的類

          // 狀態(tài) (弱光、強(qiáng)光、關(guān)燈)
          class State {
              constructor(state) {
                  this.state = state
              }
              handle(context) {
                  console.log(`this is ${this.state} light`)
                  context.setState(this)
              }
          }
          class Context {
              constructor() {
                  this.state = null
              }
              getState() {
                  return this.state
              }
              setState(state) {
                  this.state = state
              }
          }
          // test 
          let context = new Context()
          let weak = new State('weak')
          let strong = new State('strong')
          let off = new State('off')
          
          // 弱光
          weak.handle(context)
          console.log(context.getState())
          
          // 強(qiáng)光
          strong.handle(context)
          console.log(context.getState())
          
          // 關(guān)閉
          off.handle(context)
          console.log(context.getState())
          

          場景

          • 一個對象的行為取決于它的狀態(tài),并且它必須在運(yùn)行時刻根據(jù)狀態(tài)改變它的行為
          • 一個操作中含有大量的分支語句,而且這些分支語句依賴于該對象的狀態(tài)

          優(yōu)點(diǎn)

          • 定義了狀態(tài)與行為之間的關(guān)系,封裝在一個類里,更直觀清晰,增改方便
          • 狀態(tài)與狀態(tài)間,行為與行為間彼此獨(dú)立互不干擾
          • 用對象代替字符串來記錄當(dāng)前狀態(tài),使得狀態(tài)的切換更加一目了然

          缺點(diǎn)

          • 會在系統(tǒng)中定義許多狀態(tài)類
          • 邏輯分散

          迭代器模式

          提供一種方法順序一個聚合對象中各個元素,而又不暴露該對象的內(nèi)部表示。

          class Iterator {
              constructor(conatiner) {
                  this.list = conatiner.list
                  this.index = 0
              }
              next() {
                  if (this.hasNext()) {
                      return this.list[this.index++]
                  }
                  return null
              }
              hasNext() {
                  if (this.index >= this.list.length) {
                      return false
                  }
                  return true
              }
          }
          
          class Container {
              constructor(list) {
                  this.list = list
              }
              getIterator() {
                  return new Iterator(this)
              }
          }
          
          // 測試代碼
          let container = new Container([1, 2, 3, 4, 5])
          let iterator = container.getIterator()
          while(iterator.hasNext()) {
            console.log(iterator.next())
          }
          

          場景例子

          • Array.prototype.forEach
          • jQuery中的$.each()
          • ES6 Iterator

          特點(diǎn)

          • 訪問一個聚合對象的內(nèi)容而無需暴露它的內(nèi)部表示。
          • 為遍歷不同的集合結(jié)構(gòu)提供一個統(tǒng)一的接口,從而支持同樣的算法在不同的集合結(jié)構(gòu)上進(jìn)行操作

          總結(jié)

          對于集合內(nèi)部結(jié)果常常變化各異,不想暴露其內(nèi)部結(jié)構(gòu)的話,但又想讓客戶代碼透明的訪問其中的元素,可以使用迭代器模式

          橋接模式

          橋接模式(Bridge)將抽象部分與它的實(shí)現(xiàn)部分分離,使它們都可以獨(dú)立地變化。

          class Color {
              constructor(name){
                  this.name = name
              }
          }
          class Shape {
              constructor(name,color){
                  this.name = name
                  this.color = color 
              }
              draw(){
                  console.log(`${this.color.name} ${this.name}`)
              }
          }
          
          //測試
          let red = new Color('red')
          let yellow = new Color('yellow')
          let circle = new Shape('circle', red)
          circle.draw()
          let triangle = new Shape('triangle', yellow)
          triangle.draw()
          

          優(yōu)點(diǎn)

          • 有助于獨(dú)立地管理各組成部分, 把抽象化與實(shí)現(xiàn)化解耦
          • 提高可擴(kuò)充性

          缺點(diǎn)

          • 大量的類將導(dǎo)致開發(fā)成本的增加,同時在性能方面可能也會有所減少。

          組合模式

          • 將對象組合成樹形結(jié)構(gòu),以表示“整體-部分”的層次結(jié)構(gòu)。
          • 通過對象的多態(tài)表現(xiàn),使得用戶對單個對象和組合對象的使用具有一致性。
          class TrainOrder {
          	create () {
          		console.log('創(chuàng)建火車票訂單')
          	}
          }
          class HotelOrder {
          	create () {
          		console.log('創(chuàng)建酒店訂單')
          	}
          }
          
          class TotalOrder {
          	constructor () {
          		this.orderList = []
          	}
          	addOrder (order) {
          		this.orderList.push(order)
          		return this
          	}
          	create () {
          		this.orderList.forEach(item => {
          			item.create()
          		})
          		return this
          	}
          }
          // 可以在購票網(wǎng)站買車票同時也訂房間
          let train = new TrainOrder()
          let hotel = new HotelOrder()
          let total = new TotalOrder()
          total.addOrder(train).addOrder(hotel).create()
          

          場景

          • 表示對象-整體層次結(jié)構(gòu)
          • 希望用戶忽略組合對象和單個對象的不同,用戶將統(tǒng)一地使用組合結(jié)構(gòu)中的所有對象(方法)

          缺點(diǎn)

          如果通過組合模式創(chuàng)建了太多的對象,那么這些對象可能會讓系統(tǒng)負(fù)擔(dān)不起。

          原型模式

          原型模式(prototype)是指用原型實(shí)例指向創(chuàng)建對象的種類,并且通過拷貝這些原型創(chuàng)建新的對象。

          class Person {
            constructor(name) {
              this.name = name
            }
            getName() {
              return this.name
            }
          }
          class Student extends Person {
            constructor(name) {
              super(name)
            }
            sayHello() {
              console.log(`Hello, My name is ${this.name}`)
            }
          }
          
          let student = new Student("xiaoming")
          student.sayHello()
          

          原型模式,就是創(chuàng)建一個共享的原型,通過拷貝這個原型來創(chuàng)建新的類,用于創(chuàng)建重復(fù)的對象,帶來性能上的提升。

          策略模式

          定義一系列的算法,把它們一個個封裝起來,并且使它們可以互相替換

          <html>
          <head>
              <title>策略模式-校驗表單</title>
              <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
          </head>
          <body>
              <form id = "registerForm" method="post" action="http://xxxx.com/api/register">
                  用戶名:<input type="text" name="userName">
                  密碼:<input type="text" name="password">
                  手機(jī)號碼:<input type="text" name="phoneNumber">
                  <button type="submit">提交</button>
              </form>
              <script type="text/javascript">
                  // 策略對象
                  const strategies = {
                    isNoEmpty: function (value, errorMsg) {
                      if (value === '') {
                        return errorMsg;
                      }
                    },
                    isNoSpace: function (value, errorMsg) {
                      if (value.trim() === '') {
                        return errorMsg;
                      }
                    },
                    minLength: function (value, length, errorMsg) {
                      if (value.trim().length < length) {
                        return errorMsg;
                      }
                    },
                    maxLength: function (value, length, errorMsg) {
                      if (value.length > length) {
                        return errorMsg;
                      }
                    },
                    isMobile: function (value, errorMsg) {
                      if (!/^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|17[7]|18[0|1|2|3|5|6|7|8|9])\d{8}$/.test(value)) {
                        return errorMsg;
                      }                
                    }
                  }
                  
                  // 驗證類
                  class Validator {
                    constructor() {
                      this.cache = []
                    }
                    add(dom, rules) {
                      for(let i = 0, rule; rule = rules[i++];) {
                        let strategyAry = rule.strategy.split(':')
                        let errorMsg = rule.errorMsg
                        this.cache.push(() => {
                          let strategy = strategyAry.shift()
                          strategyAry.unshift(dom.value)
                          strategyAry.push(errorMsg)
                          return strategies[strategy].apply(dom, strategyAry)
                        })
                      }
                    }
                    start() {
                      for(let i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
                        let errorMsg = validatorFunc()
                        if (errorMsg) {
                          return errorMsg
                        }
                      }
                    }
                  }
          
                  // 調(diào)用代碼
                  let registerForm = document.getElementById('registerForm')
          
                  let validataFunc = function() {
                    let validator = new Validator()
                    validator.add(registerForm.userName, [{
                      strategy: 'isNoEmpty',
                      errorMsg: '用戶名不可為空'
                    }, {
                      strategy: 'isNoSpace',
                      errorMsg: '不允許以空白字符命名'
                    }, {
                      strategy: 'minLength:2',
                      errorMsg: '用戶名長度不能小于2位'
                    }])
                    validator.add(registerForm.password, [ {
                      strategy: 'minLength:6',
                      errorMsg: '密碼長度不能小于6位'
                    }])
                    validator.add(registerForm.phoneNumber, [{
                      strategy: 'isMobile',
                      errorMsg: '請輸入正確的手機(jī)號碼格式'
                    }])
                    return validator.start()
                  }
          
                  registerForm.onsubmit = function() {
                    let errorMsg = validataFunc()
                    if (errorMsg) {
                      alert(errorMsg)
                      return false
                    }
                  }
              </script>
          </body>
          </html>

          場景例子

          • 如果在一個系統(tǒng)里面有許多類,它們之間的區(qū)別僅在于它們的'行為',那么使用策略模式可以動態(tài)地讓一個對象在許多行為中選擇一種行為。
          • 一個系統(tǒng)需要動態(tài)地在幾種算法中選擇一種。
          • 表單驗證

          優(yōu)點(diǎn)

          • 利用組合、委托、多態(tài)等技術(shù)和思想,可以有效的避免多重條件選擇語句
          • 提供了對開放-封閉原則的完美支持,將算法封裝在獨(dú)立的strategy中,使得它們易于切換,理解,易于擴(kuò)展
          • 利用組合和委托來讓Context擁有執(zhí)行算法的能力,這也是繼承的一種更輕便的代替方案

          缺點(diǎn)

          • 會在程序中增加許多策略類或者策略對象
          • 要使用策略模式,必須了解所有的strategy,必須了解各個strategy之間的不同點(diǎn),這樣才能選擇一個合適的strategy

          享元模式

          運(yùn)用共享技術(shù)有效地支持大量細(xì)粒度對象的復(fù)用。系統(tǒng)只使用少量的對象,而這些對象都很相似,狀態(tài)變化很小,可以實(shí)現(xiàn)對象的多次復(fù)用。由于享元模式要求能夠共享的對象必須是細(xì)粒度對象,因此它又稱為輕量級模式,它是一種對象結(jié)構(gòu)型模式

          let examCarNum = 0         // 駕考車總數(shù)
          /* 駕考車對象 */
          class ExamCar {
              constructor(carType) {
                  examCarNum++
                  this.carId = examCarNum
                  this.carType = carType ? '手動檔' : '自動檔'
                  this.usingState = false    // 是否正在使用
              }
          
              /* 在本車上考試 */
              examine(candidateId) {
                  return new Promise((resolve => {
                      this.usingState = true
                      console.log(`考生- ${ candidateId } 開始在${ this.carType }駕考車- ${ this.carId } 上考試`)
                      setTimeout(() => {
                          this.usingState = false
                          console.log(`%c考生- ${ candidateId } 在${ this.carType }駕考車- ${ this.carId } 上考試完畢`, 'color:#f40')
                          resolve()                       // 0~2秒后考試完畢
                      }, Math.random() * 2000)
                  }))
              }
          }
          
          /* 手動檔汽車對象池 */
          ManualExamCarPool = {
              _pool: [],                  // 駕考車對象池
              _candidateQueue: [],        // 考生隊列
          
              /* 注冊考生 ID 列表 */
              registCandidates(candidateList) {
                  candidateList.forEach(candidateId => this.registCandidate(candidateId))
              },
          
              /* 注冊手動檔考生 */
              registCandidate(candidateId) {
                  const examCar = this.getManualExamCar()    // 找一個未被占用的手動檔駕考車
                  if (examCar) {
                      examCar.examine(candidateId)           // 開始考試,考完了讓隊列中的下一個考生開始考試
                        .then(() => {
                            const nextCandidateId = this._candidateQueue.length && this._candidateQueue.shift()
                            nextCandidateId && this.registCandidate(nextCandidateId)
                        })
                  } else this._candidateQueue.push(candidateId)
              },
          
              /* 注冊手動檔車 */
              initManualExamCar(manualExamCarNum) {
                  for (let i = 1; i <= manualExamCarNum; i++) {
                      this._pool.push(new ExamCar(true))
                  }
              },
          
              /* 獲取狀態(tài)為未被占用的手動檔車 */
              getManualExamCar() {
                  return this._pool.find(car => !car.usingState)
              }
          }
          
          ManualExamCarPool.initManualExamCar(3)          // 一共有3個駕考車
          ManualExamCarPool.registCandidates([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])  // 10個考生來考試
          

          場景例子

          • 文件上傳需要創(chuàng)建多個文件實(shí)例的時候
          • 如果一個應(yīng)用程序使用了大量的對象,而這些大量的對象造成了很大的存儲開銷時就應(yīng)該考慮使用享元模式

          優(yōu)點(diǎn)

          • 大大減少對象的創(chuàng)建,降低系統(tǒng)的內(nèi)存,使效率提高。

          缺點(diǎn)

          • 提高了系統(tǒng)的復(fù)雜度,需要分離出外部狀態(tài)和內(nèi)部狀態(tài),而且外部狀態(tài)具有固有化的性質(zhì),

          不應(yīng)該隨著內(nèi)部狀態(tài)的變化而變化,否則會造成系統(tǒng)的混亂

          模板方法模式

          模板方法模式由兩部分結(jié)構(gòu)組成,第一部分是抽象父類,第二部分是具體的實(shí)現(xiàn)子類。通常在抽象父類中封裝了子類的算法框架,包括實(shí)現(xiàn)一些公共方法和封裝子類中所有方法的執(zhí)行順序。子類通過繼承這個抽象類,也繼承了整個算法結(jié)構(gòu),并且可以選擇重寫父類的方法。

          class Beverage {
              constructor({brewDrink, addCondiment}) {
                  this.brewDrink = brewDrink
                  this.addCondiment = addCondiment
              }
              /* 燒開水,共用方法 */
              boilWater() { console.log('水已經(jīng)煮沸=== 共用') }
              /* 倒杯子里,共用方法 */
              pourCup() { console.log('倒進(jìn)杯子里===共用') }
              /* 模板方法 */
              init() {
                  this.boilWater()
                  this.brewDrink()
                  this.pourCup()
                  this.addCondiment()
              }
          }
          /* 咖啡 */
          const coffee = new Beverage({
               /* 沖泡咖啡,覆蓋抽象方法 */
               brewDrink: function() { console.log('沖泡咖啡') },
               /* 加調(diào)味品,覆蓋抽象方法 */
               addCondiment: function() { console.log('加點(diǎn)奶和糖') }
          })
          coffee.init() 
          

          場景例子

          • 一次性實(shí)現(xiàn)一個算法的不變的部分,并將可變的行為留給子類來實(shí)現(xiàn)
          • 子類中公共的行為應(yīng)被提取出來并集中到一個公共父類中的避免代碼重復(fù)

          優(yōu)點(diǎn)

          • 提取了公共代碼部分,易于維護(hù)

          缺點(diǎn)

          • 增加了系統(tǒng)復(fù)雜度,主要是增加了的抽象類和類間聯(lián)系

          職責(zé)鏈模式

          使多個對象都有機(jī)會處理請求,從而避免請求的發(fā)送者和接受者之間的耦合關(guān)系,將這些對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理它為止

          // 請假審批,需要組長審批、經(jīng)理審批、總監(jiān)審批
          class Action {
              constructor(name) {
                  this.name = name
                  this.nextAction = null
              }
              setNextAction(action) {
                  this.nextAction = action
              }
              handle() {
                  console.log( `${this.name} 審批`)
                  if (this.nextAction != null) {
                      this.nextAction.handle()
                  }
              }
          }
          
          let a1 = new Action("組長")
          let a2 = new Action("經(jīng)理")
          let a3 = new Action("總監(jiān)")
          a1.setNextAction(a2)
          a2.setNextAction(a3)
          a1.handle()
          

          場景例子

          • JS 中的事件冒泡
          • 作用域鏈
          • 原型鏈

          優(yōu)點(diǎn)

          • 降低耦合度。它將請求的發(fā)送者和接收者解耦。
          • 簡化了對象。使得對象不需要知道鏈的結(jié)構(gòu)
          • 增強(qiáng)給對象指派職責(zé)的靈活性。通過改變鏈內(nèi)的成員或者調(diào)動它們的次序,允許動態(tài)地新增或者刪除責(zé)任
          • 增加新的請求處理類很方便。

          缺點(diǎn)

          • 不能保證某個請求一定會被鏈中的節(jié)點(diǎn)處理,這種情況可以在鏈尾增加一個保底的接受者節(jié)點(diǎn)來處理這種即將離開鏈尾的請求。
          • 使程序中多了很多節(jié)點(diǎn)對象,可能再一次請求的過程中,大部分的節(jié)點(diǎn)并沒有起到實(shí)質(zhì)性的作用。他們的作用僅僅是讓請求傳遞下去,從性能當(dāng)面考慮,要避免過長的職責(zé)鏈到來的性能損耗。

          命令模式

          將一個請求封裝成一個對象,從而讓你使用不同的請求把客戶端參數(shù)化,對請求排隊或者記錄請求日志,可以提供命令的撤銷和恢復(fù)功能。

          // 接收者類
          class Receiver {
              execute() {
                console.log('接收者執(zhí)行請求')
              }
            }
            
          // 命令者
          class Command {  
              constructor(receiver) {
                  this.receiver = receiver
              }
              execute () {    
                  console.log('命令');
                  this.receiver.execute()
              }
          }
          // 觸發(fā)者
          class Invoker {   
              constructor(command) {
                  this.command = command
              }
              invoke() {   
                  console.log('開始')
                  this.command.execute()
              }
          }
            
          // 倉庫
          const warehouse = new Receiver();   
          // 訂單    
          const order = new Command(warehouse);  
          // 客戶
          const client = new Invoker(order);      
          client.invoke()
          

          優(yōu)點(diǎn)

          • 對命令進(jìn)行封裝,使命令易于擴(kuò)展和修改
          • 命令發(fā)出者和接受者解耦,使發(fā)出者不需要知道命令的具體執(zhí)行過程即可執(zhí)行

          缺點(diǎn)

          • 使用命令模式可能會導(dǎo)致某些系統(tǒng)有過多的具體命令類。

          備忘錄模式

          在不破壞封裝性的前提下,捕獲一個對象的內(nèi)部狀態(tài),并在該對象之外保存這個狀態(tài)。這樣以后就可將該對象恢復(fù)到保存的狀態(tài)。

          //備忘類
          class Memento{
              constructor(content){
                  this.content = content
              }
              getContent(){
                  return this.content
              }
          }
          // 備忘列表
          class CareTaker {
              constructor(){
                  this.list = []
              }
              add(memento){
                  this.list.push(memento)
              }
              get(index){
                  return this.list[index]
              }
          }
          // 編輯器
          class Editor {
              constructor(){
                  this.content = null
              }
              setContent(content){
                  this.content = content
              }
              getContent(){
               return this.content
              }
              saveContentToMemento(){
                  return new Memento(this.content)
              }
              getContentFromMemento(memento){
                  this.content = memento.getContent()
              }
          }
          
          //測試代碼
          
          let editor = new Editor()
          let careTaker = new CareTaker()
          
          editor.setContent('111')
          editor.setContent('222')
          careTaker.add(editor.saveContentToMemento())
          editor.setContent('333')
          careTaker.add(editor.saveContentToMemento())
          editor.setContent('444')
          
          console.log(editor.getContent()) //444
          editor.getContentFromMemento(careTaker.get(1))
          console.log(editor.getContent()) //333
          
          editor.getContentFromMemento(careTaker.get(0))
          console.log(editor.getContent()) //222
          

          場景例子

          • 分頁控件
          • 撤銷組件

          優(yōu)點(diǎn)

          • 給用戶提供了一種可以恢復(fù)狀態(tài)的機(jī)制,可以使用戶能夠比較方便地回到某個歷史的狀態(tài)

          缺點(diǎn)

          • 消耗資源。如果類的成員變量過多,勢必會占用比較大的資源,而且每一次保存都會消耗一定的內(nèi)存。

          中介者模式

          解除對象與對象之間的緊耦合關(guān)系。增加一個中介者對象后,所有的 相關(guān)對象都通過中介者對象來通信,而不是互相引用,所以當(dāng)一個對象發(fā)生改變時,只需要通知 中介者對象即可。中介者使各對象之間耦合松散,而且可以獨(dú)立地改變它們之間的交互。中介者 模式使網(wǎng)狀的多對多關(guān)系變成了相對簡單的一對多關(guān)系(類似于觀察者模式,但是單向的,由中介者統(tǒng)一管理。)

          class A {
              constructor() {
                  this.number = 0
              }
              setNumber(num, m) {
                  this.number = num
                  if (m) {
                      m.setB()
                  }
              }
          }
          class B {
              constructor() {
                  this.number = 0
              }
              setNumber(num, m) {
                  this.number = num
                  if (m) {
                      m.setA()
                  }
              }
          }
          class Mediator {
              constructor(a, b) {
                  this.a = a
                  this.b = b
              }
              setA() {
                  let number = this.b.number
                  this.a.setNumber(number * 10)
              }
              setB() {
                  let number = this.a.number
                  this.b.setNumber(number / 10)
              }
          }
          
          let a = new A()
          let b = new B()
          let m = new Mediator(a, b)
          a.setNumber(10, m)
          console.log(a.number, b.number)
          b.setNumber(10, m)
          console.log(a.number, b.number)
          

          場景例子

          • 系統(tǒng)中對象之間存在比較復(fù)雜的引用關(guān)系,導(dǎo)致它們之間的依賴關(guān)系結(jié)構(gòu)混亂而且難以復(fù)用該對象
          • 想通過一個中間類來封裝多個類中的行為,而又不想生成太多的子類。

          優(yōu)點(diǎn)

          • 使各對象之間耦合松散,而且可以獨(dú)立地改變它們之間的交互
          • 中介者和對象一對多的關(guān)系取代了對象之間的網(wǎng)狀多對多的關(guān)系
          • 如果對象之間的復(fù)雜耦合度導(dǎo)致維護(hù)很困難,而且耦合度隨項目變化增速很快,就需要中介者重構(gòu)代碼

          缺點(diǎn)

          • 系統(tǒng)中會新增一個中介者對象,因 為對象之間交互的復(fù)雜性,轉(zhuǎn)移成了中介者對象的復(fù)雜性,使得中介者對象經(jīng)常是巨大的。中介 者對象自身往往就是一個難以維護(hù)的對象。

          解釋器模式

          給定一個語言, 定義它的文法的一種表示,并定義一個解釋器, 該解釋器使用該表示來解釋語言中的句子。

          class Context {
              constructor() {
                this._list = []; // 存放 終結(jié)符表達(dá)式
                this._sum = 0; // 存放 非終結(jié)符表達(dá)式(運(yùn)算結(jié)果)
              }
            
              get sum() {
                return this._sum;
              }
              set sum(newValue) {
                this._sum = newValue;
              }
              add(expression) {
                this._list.push(expression);
              }
              get list() {
                return [...this._list];
              }
            }
            
            class PlusExpression {
              interpret(context) {
                if (!(context instanceof Context)) {
                  throw new Error("TypeError");
                }
                context.sum = ++context.sum;
              }
            }
            class MinusExpression {
              interpret(context) {
                if (!(context instanceof Context)) {
                  throw new Error("TypeError");
                }
                context.sum = --context.sum;
              }
            }
            
            /** 以下是測試代碼 **/
            const context = new Context();
            
            // 依次添加: 加法 | 加法 | 減法 表達(dá)式
            context.add(new PlusExpression());
            context.add(new PlusExpression());
            context.add(new MinusExpression());
            
            // 依次執(zhí)行: 加法 | 加法 | 減法 表達(dá)式
            context.list.forEach(expression => expression.interpret(context));
            console.log(context.sum);
          

          優(yōu)點(diǎn)

          • 易于改變和擴(kuò)展文法。
          • 由于在解釋器模式中使用類來表示語言的文法規(guī)則,因此可以通過繼承等機(jī)制來改變或擴(kuò)展文法

          缺點(diǎn)

          • 執(zhí)行效率較低,在解釋器模式中使用了大量的循環(huán)和遞歸調(diào)用,因此在解釋較為復(fù)雜的句子時其速度慢
          • 對于復(fù)雜的文法比較難維護(hù)

          訪問者模式

          表示一個作用于某對象結(jié)構(gòu)中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用于這些元素的新操作。

          // 訪問者  
          class Visitor {
              constructor() {}
              visitConcreteElement(ConcreteElement) {
                  ConcreteElement.operation()
              }
          }
          // 元素類  
          class ConcreteElement{
              constructor() {
              }
              operation() {
                 console.log("ConcreteElement.operation invoked");  
              }
              accept(visitor) {
                  visitor.visitConcreteElement(this)
              }
          }
          // client
          let visitor = new Visitor()
          let element = new ConcreteElement()
          element.accept(visitor)
          

          場景例子

          • 對象結(jié)構(gòu)中對象對應(yīng)的類很少改變,但經(jīng)常需要在此對象結(jié)構(gòu)上定義新的操作
          • 需要對一個對象結(jié)構(gòu)中的對象進(jìn)行很多不同的并且不相關(guān)的操作,而需要避免讓這些操作"污染"這些對象的類,也不希望在增加新操作時修改這些類。

          優(yōu)點(diǎn)

          • 符合單一職責(zé)原則
          • 優(yōu)秀的擴(kuò)展性
          • 靈活性

          缺點(diǎn)

          • 具體元素對訪問者公布細(xì)節(jié),違反了迪米特原則
          • 違反了依賴倒置原則,依賴了具體類,沒有依賴抽象。
          • 具體元素變更比較困難

          主站蜘蛛池模板: 无码精品视频一区二区三区| 国产成人高清亚洲一区久久| 亚洲日本一区二区| 国产情侣一区二区三区| 中日韩一区二区三区| 国产综合无码一区二区辣椒| 一区二区三区在线|欧| 国产精品电影一区| 国产精品综合一区二区| 中文字幕一区二区三区永久| 风间由美性色一区二区三区| 精品欧洲av无码一区二区| 日本一区二区三区久久| 熟女性饥渴一区二区三区| 午夜影院一区二区| 日韩在线一区二区三区免费视频| 亚欧在线精品免费观看一区| 在线视频亚洲一区| 日韩一区二区久久久久久| 怡红院AV一区二区三区| 国产精品亚洲产品一区二区三区 | 无码一区二区三区AV免费| 搜日本一区二区三区免费高清视频 | 91久久精品午夜一区二区| 老熟妇仑乱视频一区二区 | 在线视频一区二区三区四区| 久久一区二区免费播放| 无码国产精品一区二区免费式影视 | 99久久精品国产免看国产一区 | 乱中年女人伦av一区二区| 免费av一区二区三区| 成人无码精品一区二区三区| 国产波霸爆乳一区二区| 中文字幕日韩丝袜一区| 国产vr一区二区在线观看| 一本一道波多野结衣一区| 色狠狠色噜噜Av天堂一区| 国产第一区二区三区在线观看| 中文字幕精品一区二区三区视频| 亚洲一区二区三区精品视频| 国产免费播放一区二区|