Node.js && JavaScript 面試經常使用的設計模式二

觀察者模式

這是一個很是有趣的模式,它容許你經過對某個輸入做出反應來響應,而不是主動地檢查是否提供了輸入。換句話說,使用此模式,能夠指定你正在等待的輸入類型,並被動地等待,直到提供該輸入才執行代碼。git

在這裏,觀察者是一個對象,它們知道想要接收的輸入類型和要響應的操做,這些對象的做用是「觀察」另外一個對象並等待它與它們通訊。github

另外一方面,可觀察對象將讓觀察者知道什麼時候有新的輸入可用,以便他們能夠對它作出反應(若是適用的話)。若是這聽起來很熟悉,那是由於Node.js中處理事件的任何東西都是這種模式。數據庫

下面的代碼是一段HTTP Server的實現express

const http = require('http');

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Your own server here');
})

server.on('error', err => {
    console.log(「Error:: 「, err)
})

server.listen(3000, '127.0.0.1', () => {
  console.log('Server up and running');
})
複製代碼

在上面的代碼中隱藏了觀察者模式。服務器對象將充當可觀察對象,而回調函數是實際的觀察者。你能夠看到,這種模式很是適合這種HTTP異步調用。這種模式的另外一個普遍使用的用例是觸發特定事件。這種模式能夠在任何容易異步觸發事件(例如錯誤或狀態更新)的模塊上找到。例如數據庫驅動程序,甚至套接字。io,它容許你在本身代碼以外觸發的特定事件上設置觀察者。服務器

下面咱們簡單的實現一個EventEmitter類來實現觀察者模式:app

class eventEmitter {
  constructor() {
    this.eventObj = {}
  }
  on(evName, fn) {
    this.eventObj[evName] = this.eventObj[evName] ? this.eventObj[evName].concat(fn) : [fn]
  }
  emit(evName, ...params) {
    for (let fn of this.eventObj[evName]) {
      fn.apply(null, params)
    }
  }
}
複製代碼
const event = new eventEmitter()
event.on('error', err => {
  console.log(err, 1)
})
event.on('error', err => {
  console.log(err, 2)
})

event.emit('error')
複製代碼

上面的例子中,error事件做爲一個被觀察對象,回調函數是觀察者,當觸發on事件時,觀察者會被加入到一個隊列中,當error事件觸發時,回調函數(觀察者)會受到通知,而且依次被調用。異步

職責鏈模式

職責鏈模式是Node.js世界中不少人使用過的一種模式,他們甚至沒有意識到這一點。函數

使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係,將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。post

下面是一個很是基本的實現這個模式,你能夠看到在底部,咱們有四個可能的值須要處理,可是咱們不在意誰來處理它們,咱們只須要,至少,一個函數來使用它們,所以咱們只是寄給鏈,讓鏈中的每個函數決定他們是否應該使用它或忽略它。ui

function processRequest(r, chain) {

    let lastResult = null
    let i = 0
    do {
     lastResult = chain[i](r)
     i++
    } while(lastResult != null && i < chain.length)
    if(lastResult != null) {
     console.log("Error: request could not be fulfilled")
    }
}

let chain = [
    function (r) {
     if(typeof r == 'number') {
         console.log("It's a number: ", r)
         return null
     }
     return r
    },
    function (r) {
     if(typeof r == 'string') {
         console.log("It's a string: ", r)
         return null
     }
     return r
    },
    function (r) {
     if(Array.isArray(r)) {
         console.log("It's an array of length: ", r.length)
         return null
     }
     return r
    }
]

processRequest(1, chain)
processRequest([1,2,3], chain)
processRequest('[1,2,3]', chain)
processRequest({}, chain)
複製代碼

輸出的結果是:

It's a number:  1
It's an array of length:  3
It's a string:  [1,2,3]
Error: request could not be fulfilled
複製代碼

使用例子

在咱們常常使用的開發庫中,這種模式最明顯的例子是ExpressJS的中間件。使用該模式,其實是在設置一系列函數(中間件),這些函數計算請求對象並決定對其運行一個函數或者忽略它。能夠將該模式看做上面示例的異步版本,在這個版本中,不是檢查函數是否返回值,而是檢查將哪些值傳遞給它們調用的下一個回調。

var app = express();

app.use(function (req, res, next) {
  console.log('Time:', Date.now())
  next(); //call the next function on the chain
})
複製代碼

中間件是這種模式的一種特殊實現,由於能夠認爲全部中間件均可以完成請求,而不是鏈中的一個成員。然而,其背後的原理是同樣的。

下面咱們來本身簡單實現一箇中間件函數

class App {
  constructor() {
    this.middleware = []
    this.index = 0
  }

  use(fn) {
    this.middleware.push(fn)
  }

  exec() {
    this.next()
  }

  next() {
    if (this.index < this.middleware.length) {
      const fn = this.middleware[this.index]
      this.index++
      fn.call(this, this.next.bind(this))
    }
  }
}

const app = new App()

app.use(function (next) {
  console.log(1)
  next()
})

app.use(function (next) {
  console.log(2)
})

app.exec()
複製代碼

輸出結果

1,2
複製代碼

當使用use函數時,在中間件隊列中加入回調函數,在執行時,能夠調用next()來進入下一個中間件。

上一篇咱們理解了IIFE工廠模式還有單例模式

關注做者github,第一時間得到更新。

相關文章
相關標籤/搜索