[深入23] JS設(shè)計(jì)模式 - 代理盐固,策略妹孙,單例

導(dǎo)航

[react] Hooks

[React 從零實(shí)踐01-后臺(tái)] 代碼分割
[React 從零實(shí)踐02-后臺(tái)] 權(quán)限控制
[React 從零實(shí)踐03-后臺(tái)] 自定義hooks
[React 從零實(shí)踐04-后臺(tái)] docker-compose 部署react+egg+nginx+mysql
[React 從零實(shí)踐05-后臺(tái)] Gitlab-CI使用Docker自動(dòng)化部署

[源碼-webpack01-前置知識(shí)] AST抽象語(yǔ)法樹(shù)
[源碼-webpack02-前置知識(shí)] Tapable
[源碼-webpack03] 手寫(xiě)webpack - compiler簡(jiǎn)單編譯流程
[源碼] Redux React-Redux01
[源碼] axios
[源碼] vuex
[源碼-vue01] data響應(yīng)式 和 初始化渲染
[源碼-vue02] computed 響應(yīng)式 - 初始化罗标,訪(fǎng)問(wèn)庸队,更新過(guò)程
[源碼-vue03] watch 偵聽(tīng)屬性 - 初始化和更新
[源碼-vue04] Vue.set 和 vm.$set
[源碼-vue05] Vue.extend

[源碼-vue06] Vue.nextTick 和 vm.$nextTick
[部署01] Nginx
[部署02] Docker 部署vue項(xiàng)目
[部署03] gitlab-CI

[數(shù)據(jù)結(jié)構(gòu)和算法01] 二分查找和排序

[深入01] 執(zhí)行上下文
[深入02] 原型鏈
[深入03] 繼承
[深入04] 事件循環(huán)
[深入05] 柯里化 偏函數(shù) 函數(shù)記憶
[深入06] 隱式轉(zhuǎn)換 和 運(yùn)算符
[深入07] 瀏覽器緩存機(jī)制(http緩存機(jī)制)
[深入08] 前端安全
[深入09] 深淺拷貝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模塊化
[深入13] 觀察者模式 發(fā)布訂閱模式 雙向數(shù)據(jù)綁定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手寫(xiě)Promise
[深入20] 手寫(xiě)函數(shù)
[深入21] 數(shù)據(jù)結(jié)構(gòu)和算法 - 二分查找和排序
[深入22] js和v8垃圾回收機(jī)制
[深入23] JS設(shè)計(jì)模式 - 代理,策略闯割,單例

(一) 前置知識(shí)

  • 設(shè)計(jì)模式一直都沒(méi)敢寫(xiě)彻消,主要是個(gè)人覺(jué)得理解不難,主要膽怯在運(yùn)用于生產(chǎn)項(xiàng)目

(1) 一些單詞

pattern: 模式 // design pattern: 設(shè)計(jì)模式
strategy: 策略宙拉,戰(zhàn)略

performance: 性能宾尚,績(jī)效
salary: 工資,薪水
bonus: 獎(jiǎng)金

singleton: 單身谢澈,單例模式

(2) Storage

  • 概念
    • 兩個(gè)對(duì)象部署了 ( Storage接口 )煌贴,分別是 ( window.localStorage ) 和 ( window.sesstionStorage )
  • 方法
    • Storage.setItem(key, value)
      • key 必須是符串,非字符串會(huì)被轉(zhuǎn)成字符串
      • value 也必須是字符串澳化,非字符串會(huì)被轉(zhuǎn)成字符串
      • 如果key已經(jīng)存在崔步,則新的value覆蓋舊的value
      • 如果存儲(chǔ)空間已滿(mǎn),該方法會(huì)拋錯(cuò)
      • Storage.setItem('name', 'woow_wu7') 等價(jià)于 Storage.name = 'woow_wu7'
    • Storage.getItem(key)
      • key不存在缎谷,則返回 null
    • Storage.removeItem(key)
      • 清除key對(duì)應(yīng)的value
    • Storage.clear()
      • 清除Storage中的所有數(shù)據(jù)
      • 返回值是undefined
    • Storage.key(number)
      • 接受一個(gè)整數(shù)作為參數(shù),返回該位置對(duì)應(yīng)的鍵值
  • 事件
    • window.addEventListener('storage', onStorageChange)
    • 該監(jiān)聽(tīng)函數(shù)的event對(duì)象
      • StorageEvent.key: 發(fā)生變動(dòng)的鍵名
      • StorageEvent.newValue: 新的鍵值,字符串
      • Storage.oleValue: 舊的鍵值列林,字符串
      • Storage.storageArea: 返回整個(gè)對(duì)象
      • Storage.url: 表示原始觸發(fā) storage 事件的 ( 網(wǎng)頁(yè)地址 )
  • 特別注意
    • 該事件不在導(dǎo)致數(shù)據(jù)變化的當(dāng)前頁(yè)面觸發(fā)瑞你,而是在 ( 同一個(gè)域名 ) 的其他窗口觸發(fā)
    • 所以:如果瀏覽器只有一個(gè)窗口,可能看不到該事件觸發(fā)
    • 所以:可以實(shí)現(xiàn)多窗口通信
      image

(3) 對(duì)象的命令空間

var namespace = {
  a: () => { console.log('a') },
  b: () => { console.log('b') },
}

(二) JS設(shè)計(jì)模式

(1) 代理模式

(1) 代理模式的定義

  • 代理模式: 指給某一個(gè)對(duì)象提供一個(gè) ( 代理對(duì)象 )希痴,并由 ( 代理對(duì)象 ) 來(lái)控制 ( 原對(duì)象的引用 )者甲,代理模式類(lèi)似 ( 中介 )

(2) 為什么要使用代理模式?

  • 中介隔離
    • ( 客戶(hù)對(duì)象 ) === ( 代理對(duì)象 ) === ( 委托對(duì)象 )
    • 一些情況下砌创,客戶(hù)對(duì)象不想或者不能直接引用一個(gè)委托對(duì)象虏缸,則代理對(duì)象可以充當(dāng)中介作用
  • 開(kāi)閉原則,增加功能
    • ( 代理類(lèi) ) 除了是 ( 客戶(hù)類(lèi) ) 和 ( 委托類(lèi) ) 的中介之外嫩实,還可以給代理類(lèi)增加額外的功能來(lái)擴(kuò)展委托類(lèi)的功能刽辙,這樣我們就可以直接修改代理類(lèi)而不是直接修改委托類(lèi),符合代碼設(shè)計(jì)的開(kāi)閉原則
    • ( 代理類(lèi) ) 主要負(fù)責(zé)委托類(lèi)的 ( 預(yù)處理消息甲献, 過(guò)濾消息宰缤, 轉(zhuǎn)發(fā)消息給委托類(lèi),對(duì)返回結(jié)果的處理 )
    • ( 代理類(lèi) ) 本身不提供服務(wù)晃洒,而是通過(guò)調(diào)用委托類(lèi)相關(guān)的方法慨灭,來(lái)提供特定的服務(wù),真正的業(yè)務(wù)功能還是由委托類(lèi)來(lái)實(shí)現(xiàn)

(3) 實(shí)戰(zhàn)

(3-1) 代理ajax請(qǐng)求 - 增加緩存功能

  • 作用
    • 通過(guò) 代理函數(shù)proxyRequest() 調(diào)用 cache() 使得 request() 方法做了一層緩存
    • 緩存每次請(qǐng)求的參數(shù)球及,如果參數(shù)一樣氧骤,就直接返回之前參數(shù)對(duì)應(yīng)的 Map 中的緩存的 value
  • 原理
    • 利用 ( 代理模式 ) 給原由的函數(shù)添加新的邏輯但又不影響原函數(shù),相當(dāng)于用 ( 函數(shù)組合 ) 來(lái)實(shí)現(xiàn) ( 復(fù)用邏輯和添加邏輯 )
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    // Map
    // 1.實(shí)例屬性 mapInstance.size
    // 2.原型方法 mapInstance.get() set() has() delete() clear()
    const mapInstance = new Map()

    // request 發(fā)送請(qǐng)求吃引,返回一個(gè)promise實(shí)例
    const request = (params) => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          return resolve(`data: ${params}`)
        }, 2000)
      })
    }

    // cache 緩存 ( 請(qǐng)求參數(shù) )和 ( 請(qǐng)求結(jié)果 promise )
    const cache = (params) => {
      if (mapInstance.has(params)) { // Map.prototype.has
        return mapInstance.get(params) // Map.prototype.get
      }
      mapInstance.set(params, request(params))
      return request(params)
    }

    // proxyRequest 代理請(qǐng)求
    const proxyRequest = (params) => {
      return cache(params)
    }
    
    // 當(dāng)參數(shù)一樣時(shí)语淘,是從Map中返回的緩存值
    proxyRequest('11').then(data1 => {
      proxyRequest('11').then(data2 => console.log('data1 === data2 ? 這里返回true ', data1 === data2))
    })
  </script>
</body>
</html>

(3-2) 緩存代理 - 處理緩存過(guò)期時(shí)間 - class版本

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    class Storage {
      constructor({ type, expires }) {
        this.type = type // 類(lèi)型
        this.expires = expires // 過(guò)期時(shí)間
        this.storageType = {
          local: 'localStorage',
          session: 'sessionStorage'
        }
      }
      // 取
      // 代理原生 getItem() 方法,根據(jù)緩存過(guò)期時(shí)間际歼,判斷數(shù)據(jù)是否過(guò)期
      getItem(key) {
        const now = +new Date()
        const { value, setTimes } = JSON.parse(window[this.storageType[this.type]].getItem(key))
        if (now > setTimes + this.expires) {
          window[this.storageType[this.type]].removeItem(key) // 如果過(guò)期惶翻,清除該 Storage
          return null
        }
        return value
      }
      // 存
      // 代理原生的 setItem() 方法,添加緩存時(shí)間
      setItem(key, value) {
        window[this.storageType[this.type]].setItem(key, JSON.stringify({
          value,
          setTimes: +new Date()
        }))
      }
    }
    const localStorage = new Storage({type: 'local', expires: 1000})

    localStorage.setItem('name', 'woow_wu7')
    console.log('未過(guò)期', localStorage.getItem('name'))

    setTimeout(() => {
    console.log('過(guò)期', localStorage.getItem('name'))
    }, 2000)
    
  </script>
</body>
</html>
image

2021/05/03 優(yōu)化

(3-2) 優(yōu)化緩存代理 - 處理緩存過(guò)期時(shí)間 - class版

<!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>
      class Storage {
        constructor(type, expires) {
          this.storage = window[type];
          this.expires = expires;
        }
        // 存
        // 代理原生的 setItem() 方法鹅心,添加緩存過(guò)期時(shí)間
        setItem = (key, value) => {
          this.storage.setItem(
            key,
            JSON.stringify({
              value,
              setTime: +new Date(), // --------------------- ( 存 ) 的時(shí)間戳
            })
          );
        };
        // 取
        // 代理原生的 getItem() 方法吕粗,根據(jù)傳入的過(guò)期時(shí)間,來(lái)判斷數(shù)據(jù)是否過(guò)期
        getItem = (key) => {
          const now = +new Date(); // --------------------- ( 取 ) 的時(shí)間戳
          const { value, setTime } = JSON.parse(this.storage.getItem(key));
          if (now - setTime > this.expires) {
            this.storage.removeItem(key); // -------------- 過(guò)期刪除
          }
          return this.storage.getItem(key);
        };
      }
      const localStorageInstance = new Storage("localStorage", 5000);
      localStorageInstance.setItem("name", "woow_wu7");
      console.log("未過(guò)期", localStorageInstance.getItem("name"));
      setTimeout(() => {
        console.log("過(guò)期", localStorageInstance.getItem("name"));
      }, 6000);
    </script>
  </body>
</html>

(3-3) 緩存代理 - 處理緩存過(guò)期時(shí)間 - function版本

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    function Storage(type, expires) {
      this.type = type
      this.expires = expires
      this.storageType = {
        local: 'localStorage',
        sesstion: 'sesstionStorage'
      }       
    }
    Storage.prototype.getItem = function(key) {
      const now = +new Date()
      const { value, setTimes } = JSON.parse(window[this.storageType[this.type]].getItem(key))
      if (now - setTimes > this.expires) {
        window[this.storageType[this.type]].removeItem(key)
        return null
      }
      return value
    }
    Storage.prototype.setItem = function(key, value) {
      window[this.storageType[this.type]].setItem(key, JSON.stringify({
        value,
        setTimes: +new Date()
      }))
    }
    const storage = new Storage('local', 1000)
    storage.setItem('name', 'woow_wu7')
    console.log('未過(guò)期', storage.getItem('name'))

    setTimeout(() => {
      console.log('過(guò)期', storage.getItem('name'))
    }, 2000)
  </script>
</body>
</html>
image

(2) 策略模式

(1) 概念

  • 定義一系列算法旭愧,把它們一個(gè)個(gè)封裝起來(lái)颅筋,并使它們可以 ( 相互替換 )
  • 將 ( 不變的部分 ) 和 ( 變化的部分 ) 隔開(kāi)
  • ( 策略模式 ) 的主要目的就是將 ( 算法的使用 ) 和 ( 算法的實(shí)現(xiàn) ) 分離開(kāi)來(lái)

(2) 組成 - 策略模式組要包含 ( 策略類(lèi) ) 和 ( 環(huán)境類(lèi) )

  • 策略類(lèi)
    • 封裝了具體的算法,并負(fù)責(zé)具體的計(jì)算過(guò)程
  • 環(huán)境類(lèi) context
    • 環(huán)境類(lèi)context接受客戶(hù)的請(qǐng)求输枯,隨后把請(qǐng)求 ( 委托 ) 給某一個(gè)策略類(lèi)
    • 環(huán)境類(lèi)context中要維持對(duì)某個(gè)策略類(lèi)的引用

(3) 特點(diǎn)

  • 避免多重選擇語(yǔ)句出現(xiàn):策略模式利用 ( 組合议泵,委托,多態(tài) ) 等技術(shù)和思想桃熄,可以有效的避免 ( 多重條件選擇語(yǔ)句if...else ) 的情況
  • 符合開(kāi)放封閉原則:將算法封裝在獨(dú)立strategy策略中先口,使得它們?nèi)菀浊袚Q,理解,擴(kuò)展
  • 算法可復(fù)用:可以復(fù)用在系統(tǒng)其他地方碉京,從而避免冗余重復(fù)的工作

(4) 缺點(diǎn)

  • 必須了解所有策略:必須了解各個(gè)策略的不同點(diǎn)厢汹,才能實(shí)現(xiàn)一個(gè)特定的策略

(5) 實(shí)戰(zhàn)

(5-1) 計(jì)算獎(jiǎng)金

  • S績(jī)效 - 4倍工資
  • A績(jī)效 - 3倍工資
  • B績(jī)效 - 2倍工資
  • bonus: 獎(jiǎng)金
  • salary: 工資
  • performance: 績(jī)效,性能
  • strategy: 策略
  • strategy pattern 策略模式
(1) 未經(jīng)過(guò)任何優(yōu)化的寫(xiě)法
- 缺點(diǎn)
  - 具有很多if...else語(yǔ)句
  - 缺乏擴(kuò)展性:如要添加C績(jī)效谐宙,則需要修改內(nèi)部的函數(shù)實(shí)現(xiàn)烫葬,違反開(kāi)放封閉原則
  - 復(fù)用性差
function getBonus(performance, salary) { // bonus獎(jiǎng)金 performance績(jī)效 salary獎(jiǎng)金
  if ( performance === 'S') return 4 * salary;
  if ( performance === 'A') return 3 * salary;
  if ( performance === 'B') return 2 * salary;
}
getBonus('S', 1000) // 輸出:4000



---------------
(2) 使用 - 函數(shù)組合 - 重構(gòu)代碼
function getS (salary) { return 4 * salary }
function getA (salary) { return 3 * salary }
function getB (salary) { return 2 * salary }
function getBonus(performance, salary) {
  if ( performance === 'S') return getS(salary)
  if ( performance === 'A') return getA(salary)
  if ( performance === 'B') return getB(salary)
}
getBonus('S', 1000) // 輸出:4000



---------------
(3) 使用 - 策略模式strategyPattern - 重構(gòu)代碼
- 優(yōu)點(diǎn)
  - 消除了大量if...else語(yǔ)句
  - 所有計(jì)算獎(jiǎng)金bonus的邏輯都不在getBonus函數(shù)即環(huán)境類(lèi)context中,而是分布在各個(gè)策略對(duì)象strategy中
  - context環(huán)境類(lèi)不負(fù)責(zé)計(jì)算凡蜻,只是負(fù)責(zé)將請(qǐng)求委托給strategy策略類(lèi)
const strategy = {
  S: (salary) => 4 * salary,
  A: (salary) => 3 * salary,
  B: (salary) => 2 * salary,
}
const getBonus = (performance, salary) => strategy[performance](salary)
getBonus('S', 1000) // 輸出:4000

(5-2) 表單驗(yàn)證

  • 未優(yōu)化的代碼如下
(1) 未優(yōu)化的代碼如下

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <form action="javascript:void(0)" method="post">
    <input type="text" name="username">
    <input type="password" name="password">
    <button>表單提交</button>
  </form>
  <script>
    const form = document.querySelector('form')
    form.onsubmit = () => {
      // form.username可以訪(fǎng)問(wèn)到 name="username" 的 input元素節(jié)點(diǎn)
      if (form.username.value === '') {
        console.log('用戶(hù)名不能為空')
        return false
      }
      if (form.password.value.length < 6) {
        console.log('密碼長(zhǎng)度不能少于6位')
        return false
      }
    }
  </script>
</body>
</html>
  • 使用策略模式 - 優(yōu)化表達(dá)驗(yàn)證
    • 第一步:將驗(yàn)證邏輯封裝到 ( 策略對(duì)象strategy中 )
    • 第二步:將用戶(hù)的請(qǐng)求通過(guò) ( 環(huán)境對(duì)象context ) 委托給策略對(duì)象strategy來(lái)處理
(2) 使用策略模式 - 優(yōu)化表達(dá)驗(yàn)證

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <form action="javascript:void(0)" method="post">
    <input type="text" name="username">
    <input type="password" name="password">
    <button>表單提交</button>
  </form>
  <script>
    // 策略對(duì)象 strategy
    // 注意:
    // 1. 這里每個(gè)方法可以能有 ( 兩個(gè) ) 或者 ( 三個(gè)參數(shù) )
    // 2. 很巧妙的利用了 
    //    - "isEmpty".split(';') => ["isEmpth"].shift() => [].unshift(value) => [value].push(err) => [value, err]
    //    - "minLength:6".split(";") => ["minLength", "6"].shift() => ['6'].unshift(value) => [value, '6'].push(err) => [value, '6', err]
    //    - 最后就是兩種情況: [value, err] 或 [value, minLength, err]
    // ------------------------------------ strategys
    const strategys = {
      notEmpty: (value, err) => { if (value === '') return err },
      minLength: (value, minLength, err) => { if (value.length < minLength) return err },
    }

    // 環(huán)境類(lèi) context
    // ------------------------------------ Validator
    class Validator {
      constructor() {
        this.rules = [] // 規(guī)則數(shù)組搭综,用于存放所有的規(guī)則函數(shù)
      }
      add = (dom, rule, err) => {
        const ruleToArr = rule.split(':') // ['isEmpty'] 或 ['minLength', 6]
        this.rules.push(() => { // 每次沒(méi)push一個(gè)匿名函數(shù)到this.rules中
          const strategy = ruleToArr.shift() // 'isEmpty' 或 'minLength'
          ruleToArr.unshift(dom.value) // 頭部添加
          ruleToArr.push(err) // 尾部添加
          return strategys[strategy].apply(null, ruleToArr) // ruleToArr = [value, err] 或 [value, minLength, err]
        })
      }
      start = () => {
        for(let i = 0; i < this.rules.length; i++) { // 遍歷執(zhí)行每一個(gè)函數(shù)
          let err = this.rules[i]();
          if (err) { return err }
        }
      }
    }
    
    // ------------------------------------ form元素節(jié)點(diǎn)
    const form = document.querySelector('form')
    form.onsubmit = (e) => {
      e.preventDefault()
      const validator = new Validator()
      validator.add(form.username, 'notEmpty', '用戶(hù)名不能為空') // add
      validator.add(form.password, 'minLength:6', '密碼不能少于6位') // add
      const err = validator.start() // start
      if (err) {
        console.log('err', err)
        return false
      }
    }
  </script>
</body>
</html>
image

(3) 單例模式 singleton

(1) 定義

  • 保證一個(gè)類(lèi)只有一個(gè)實(shí)例,并提供一個(gè)訪(fǎng)問(wèn)它的全局訪(fǎng)問(wèn)點(diǎn)
  • 優(yōu)點(diǎn)
    • ( 創(chuàng)建對(duì)象 ) 和 ( 管理單例 ) 被分布在不同的方法中

(2) 應(yīng)用場(chǎng)景

  • 全局變量
  • 連接數(shù)據(jù)庫(kù)划栓,防止多次連接或斷開(kāi)
  • 全局狀態(tài)管理redux vuex
  • 登陸框兑巾,form表單,loading層等

(3) 實(shí)現(xiàn)

(3-1) 閉包實(shí)現(xiàn)一個(gè)單例

  • 缺點(diǎn):需要調(diào)用 getInstance 函數(shù)創(chuàng)建對(duì)象
function Singleton(name) { // 構(gòu)造函數(shù), singleton: 單身, 注意:箭頭函數(shù)不能作為構(gòu)造函數(shù)茅姜,arguments闪朱,yeild命令
  this.name = name
}
Singleton.getInstance = (() => {
  let instance = null // 閉包變量
  return (name) => {
    if (!instance) {
      instance = new Singleton(name)
    }
    return instance
  }
})(); // IIFE立即調(diào)用的函數(shù)表達(dá)式,注意小括號(hào)和中括號(hào)開(kāi)頭的前一條語(yǔ)句需要加分號(hào)钻洒,或者小括號(hào)中括號(hào)前加分號(hào)

const a = Singleton.getInstance('A')
const b = Singleton.getInstance('B') // 這一次傳參沒(méi)用了
console.log('a === b', a === b) // true

(3-2) 函數(shù)實(shí)現(xiàn)一個(gè)單例

  • 缺點(diǎn):需要調(diào)用 getInstance 函數(shù)創(chuàng)建對(duì)象
function Singleton(name) {
  this.name = name
}
Singleton.getInstance = function(name) {
  // 注意:
  //  1. 這里的 this 指向的是 Singleton 奋姿,而不是 Singleton生成的實(shí)例
  if (!this.instance) {
    this.instance = new Singleton(name)
  }
  return this.instance
}
const a = Singleton.getInstance('A')
const b = Singleton.getInstance('B')
console.log('a === b', a === b)
console.log('a', a)
console.log('b', b)
console.log('Singleton.instance', Singleton.instance) // instance直接是Singleton的屬性

(3-3) 透明的單例模式

  • 優(yōu)點(diǎn):解決12中的需要調(diào)用 createInstance 函數(shù)來(lái)生成對(duì)象的缺點(diǎn),這里直接通過(guò)new調(diào)用
  • 缺點(diǎn):不符合單一職責(zé)原則素标,這個(gè)對(duì)象其實(shí)負(fù)責(zé)了兩個(gè)功能:?jiǎn)卫蛣?chuàng)建對(duì)象
var Singleton = (function() {
  var instance = null
  return function(name) {
    if (instance) return instance
    this.name = name
    return instance = this // 返回實(shí)例對(duì)象称诗,instance僅僅是為了做判斷
  }
})(); // IIFE返回一個(gè)構(gòu)造函數(shù)
const a = new Singleton('A')
const b = new Singleton('B')
console.log('a === b', a === b)
console.log('a', a)

(3-4) es6實(shí)現(xiàn)單例

class Book {
  constructor(name) {
    if (!Book.intance) {
      this.name = name
      Book.intance = this
    }
    return Book.intance
  }
}
const english = new Book('english')
const chinese = new Book('chinese')
console.log('english === english', english === english)
console.log('english', english)
console.log('chinese', chinese)

(4) 實(shí)戰(zhàn) - 登陸框

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <button id="login">登陸</button>
  <script>
    function init() {
      const button = document.getElementById('login')
      button.addEventListener('click', showModal, false)
    }
    init() // 綁定監(jiān)聽(tīng)
    
    var singleton = (() => {
      var instance = null
      return function(fn) {
        if (!instance) {
          instance = fn.call(null)
        }
        return instance
      }
    })() // IIFE單例子模式

    function createModal() {
      const div = document.createElement('div')
      div.innerHTML = '登陸框'
      div.style.setProperty('width', '300px')
      div.style.setProperty('height', '300px')
      div.style.setProperty('border', '1px solid black')
      document.body.appendChild(div)
      return div
    }

    function showModal() {
      singleton(createModal)
    }

  </script>
</body>

</html>
image

資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市头遭,隨后出現(xiàn)的幾起案子寓免,更是在濱河造成了極大的恐慌,老刑警劉巖计维,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件袜香,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡鲫惶,警方通過(guò)查閱死者的電腦和手機(jī)蜈首,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)欠母,“玉大人欢策,你說(shuō)我怎么就攤上這事∩吞剩” “怎么了踩寇?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)六水。 經(jīng)常有香客問(wèn)我俺孙,道長(zhǎng)辣卒,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任鼠冕,我火速辦了婚禮添寺,結(jié)果婚禮上胯盯,老公的妹妹穿的比我還像新娘懈费。我一直安慰自己,他們只是感情好博脑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布憎乙。 她就那樣靜靜地躺著,像睡著了一般叉趣。 火紅的嫁衣襯著肌膚如雪泞边。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,115評(píng)論 1 296
  • 那天疗杉,我揣著相機(jī)與錄音阵谚,去河邊找鬼。 笑死烟具,一個(gè)胖子當(dāng)著我的面吹牛梢什,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播朝聋,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嗡午,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了冀痕?” 一聲冷哼從身側(cè)響起荔睹,我...
    開(kāi)封第一講書(shū)人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎言蛇,沒(méi)想到半個(gè)月后僻他,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡腊尚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年吨拗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跟伏。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡丢胚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出受扳,到底是詐尸還是另有隱情携龟,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布勘高,位于F島的核電站峡蟋,受9級(jí)特大地震影響坟桅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蕊蝗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一仅乓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蓬戚,春花似錦夸楣、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至幢泼,卻和暖如春紧显,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缕棵。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工孵班, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人招驴。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓篙程,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親忽匈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子房午,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容