自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

參透Node中exports的7種設(shè)計(jì)模式

開發(fā) 前端
這篇文章試著要整理,翻譯Export This: Interface Design Patterns for Node.js Modules這篇非常值得一讀的文章。但因?yàn)檫@篇文章有些時(shí)日了,部分示例已經(jīng)不符合現(xiàn)況。故這是一篇加上小弟收集匯整而成的更新翻譯。

前言

這篇文章試著要整理,翻譯Export This: Interface Design Patterns for Node.js Modules這篇非常值得一讀的文章。

但因?yàn)檫@篇文章有些時(shí)日了,部分示例已經(jīng)不符合現(xiàn)況。故這是一篇加上小弟收集匯整而成的更新翻譯。

旅程的開始

當(dāng)你在Node中加載一個(gè)模塊,我們到底會(huì)取回什么?當(dāng)我們撰寫一個(gè)模塊時(shí)我們又有哪些選擇可以用來設(shè)計(jì)程序的界面?

在我***次學(xué)習(xí)Node的時(shí)候,發(fā)現(xiàn)在Node中有太多的方式處理這個(gè)問題,由于Javascript本身非常彈性,加上在社群中的開發(fā)者們各自都有不同的實(shí)作風(fēng)格,這讓當(dāng)時(shí)的我感到有點(diǎn)挫折。

在原文作者的學(xué)習(xí)旅程中曾持續(xù)的觀察尋找好的方式以應(yīng)用在其的工作上,在這篇文章中將會(huì)分享觀察到的Node模塊設(shè)計(jì)方式。

大略總結(jié)了7種設(shè)計(jì)模式(pattern)

  • 導(dǎo)出命名空間Namespace
  • 導(dǎo)出函式Function
  • 導(dǎo)出高階函式High-Order Function
  • 導(dǎo)出構(gòu)造函數(shù)/構(gòu)建函式Constructor
  • 導(dǎo)出單一實(shí)例物件Singleton
  • 擴(kuò)展全局物件Extend Global Object
  • 套用猴子補(bǔ)丁Monkey Patch

require,exports和module.exports

首先我們需要先聊點(diǎn)基礎(chǔ)的知識(shí)

在Node官方文件中定義了匯入一個(gè)檔案就是匯入一個(gè)模塊。

In Node.js,files and modules are in one-to-one correspondence.- Node文件

也就是所有的模塊會(huì)參考指向(Reference)一個(gè)隱式模塊物件的module.exports。當(dāng)我們使用require()時(shí)會(huì)取得的東西。同時(shí)我們也取得exports。

這個(gè)exports就是指向module.exports的參考。exports會(huì)收集該其屬性,如果module.exports沒有任何屬性就把這些數(shù)據(jù)交給module.exports,但如果module.exports已經(jīng)具備屬性的話,那么exports的所有數(shù)據(jù)都會(huì)被忽略。

為了讓您更好理解關(guān)于exports與module.exports下面的示例提供了較詳細(xì)的說明 

  1. var a = { id: 1 } 
  2. var b = a 
  3. console.log(a)// {id: 1} 
  4. console.log(b)// {id: 1} 
  5.  
  6. // b參考指向a,意味著修改b的屬性a會(huì)跟著變動(dòng) 
  7. b.id = 2 
  8. console.log(a)// {id: 2} 
  9. console.log(b)// {id: 2} 
  10.  
  11. //但如果將一個(gè)全新的物件賦予b那么參考的關(guān)系將會(huì)中斷 
  12. b = { id: 3 } 
  13. console.log(a)// {id: 2} 
  14. console.log(b)// {id: 3}  

另外比較具體的示例 

  1. /* person.js */ 
  2. exports.name = function(){ 
  3.  console.log('My name is andyyou.') 
  4. … 
  5. /* main.js */ 
  6. var person = require('./person.js') 
  7. person.name() 
  8. /* person.js */ 
  9. module.exports = 'Hey,andyyou' 
  10. exports.name = function(){ 
  11.  console.log('My name is andyyou') 
  12.  
  13. /* main.js */ 
  14. var person = require('./person.js') 
  15. // exports的屬性被忽略了 
  16. person.name()// TypeError: Object Hey,andyyou has no method 'name'  
  • exports只是指向module.exports的參考(Reference)
  • module.exports初始值為{}空物件,于是exports也會(huì)取得該空物件
  • require()回傳的是module.exports而不是exports
  • 所以您可以使用exports.property_name = something而不會(huì)使用exports = something
  • 一旦使用exports = something參考關(guān)系便會(huì)停止,也就是exports的數(shù)據(jù)都會(huì)被忽略。

本質(zhì)上我們可以理解為所有模塊都隱含實(shí)作了下面這行代碼

  1. var exports = module.exports = {} 

現(xiàn)在我們知道了,當(dāng)我們要導(dǎo)出一個(gè)function時(shí)我們得使用module.exports。

如果使用exports那個(gè)exports的內(nèi)存位置(Reference/參考)將會(huì)被修改而module.exports就不會(huì)得到其內(nèi)容。

另外,我們在許多項(xiàng)目看到下面的這行代碼

  1. exports = module.exports = something 

這行代碼作用就是確保exports在module.exports被我們復(fù)寫之后,仍可以指向相同的參考。

接著我們就可以透過module.exports來定義并導(dǎo)出一個(gè)function

  1. /* function.js */ 
  2. module.exports = function(){ 
  3.  return { name'andyyou' } 
  4.  

使用的方式則是

  1. var func = require('./function'

關(guān)于require一個(gè)很重要的行為就是它會(huì)緩存(Cache)module.exports的值,未來每一次require被調(diào)用時(shí)都會(huì)回傳相同的值。

它會(huì)根據(jù)匯入檔案的絕對路徑來緩存,所以當(dāng)我們想要模塊能夠回傳不同得值時(shí),我們就需要導(dǎo)出function,如此一來每次執(zhí)行函式時(shí)就會(huì)回傳一個(gè)新值。

下面在Node REPL中簡易的示范 

  1. $ node 
  2. > f1 = require('/Users/andyyou/Projects/export_this/function') 
  3. [Function
  4. > f2 = require('./function')//相同路徑 
  5. [Function
  6. > f1 === f2 
  7. true 
  8. > f1()=== f2() 
  9. false  

您可以觀察到require回傳了同樣的函式物件實(shí)例,但每一次調(diào)用函式回傳的物件是不同的。

更詳細(xì)的介紹可以參考官方文件,值得一讀。

現(xiàn)在,我們可以開始探討界面的設(shè)計(jì)模式(pattern)了。

導(dǎo)出命名空間

一個(gè)簡單且常用的設(shè)計(jì)模式就是導(dǎo)出一個(gè)包含數(shù)個(gè)屬性的物件,這些屬性具體的內(nèi)容主要是函式,但并不限于函式。

如此,我們就能夠透過匯入該模塊來取得這個(gè)命名空間下一系列相關(guān)的功能。

當(dāng)您匯入一個(gè)命名空間類型的模塊時(shí),我們通常會(huì)將模塊指定到某一個(gè)變數(shù),然后透過它的成員(物件屬性)來存取使用這些功能。

甚至我們也可以將這些變數(shù)成員直接指定到區(qū)域變數(shù)。

  1. var fs = require('fs') 
  2. var readFile = fs.readFile 
  3. var ReadStream = fs.ReadStream 
  4.  
  5. readFile('./file.txt',function(err,data){ 
  6.  console.log('readFile contents: %s',data) 
  7. })  

這便是fs核心模塊的做法

  1. var fs = exports 

首先將隱式exports物件設(shè)定一個(gè)區(qū)域變數(shù)(即上面提過的exports)到fs,然后透過fs的屬性使用各個(gè)功能,例如:fs.Stats = binding.Stats。

由于fs參考exports并且它是一個(gè)物件,所以當(dāng)我們r(jià)equire('fs')時(shí),我們就能夠透過屬性使用那些功能。

  1. fs.readFile = function(path,options,callback_){ 
  2.  //… 
  3.  

其他東西也是一樣的作法,例如導(dǎo)出構(gòu)造函數(shù) 

  1. fs.ReadStream = ReadStream 
  2. function ReadStream(path,options){ 
  3.  //… 
  4. ReadStream.prototype.open = function(){ 
  5.  //… 
  6.  

當(dāng)導(dǎo)出命名空間時(shí),您可以指定屬性到exports,就像fs的作法,又或者可以建立一個(gè)新的物件指派給module.exports 

  1. /* exports作法*/ 
  2. exports.verstion = '1.0' 
  3.  
  4. /*或者module.exports作法*/ 
  5. module.exports = { 
  6.  version: '1.0', 
  7.  doYourTasks: function(){ 
  8.  //… 
  9.  } 
  10.  

一個(gè)常見的作法就是透過一個(gè)根模塊(root)來匯整并導(dǎo)出其他模塊,如此一來只需要一個(gè)require便可以使用所有的模塊。

原文作者在Good Eggs工作時(shí),會(huì)將數(shù)據(jù)模型(Model)拆分成個(gè)別的模塊,并使用導(dǎo)出構(gòu)造函數(shù)的方式導(dǎo)出(請參考下文介紹),然后透過一個(gè)index檔案來集合該目錄下所有的數(shù)據(jù)模型并一起導(dǎo)出,如此一來在models命名空間下的所有數(shù)據(jù)模型都可以使用 

  1. var models = require('./models') 
  2. var User = models.User 
  3. var Product = models.Product  

在ES2015和CoffeeScript中我們甚至還可以使用解構(gòu)指派來匯入我們需要的功能 

  1. /* CoffeeScript */ 
  2. {User,Product} = require './models' 
  3.  
  4. /* ES2015 */ 
  5. import {User,Product} from './models'  

而剛剛提到的index.js大概就如下

  1. exports.User = require('./User') 
  2. exports.Person = require('./person')  

實(shí)際上這樣分開的寫法還有更精簡的寫法,我們可以透過一個(gè)小小的函式庫來匯入在同一階層中所有檔案并搭配CamelCase的命名規(guī)則導(dǎo)出。

于是在我們的index.js中看起來就會(huì)如下 

  1. module.exports = require('../lib/require_siblings')(__filename) 

導(dǎo)出函式

另外一個(gè)設(shè)計(jì)模式是導(dǎo)出函式當(dāng)作該模塊的界面。常見的作法是導(dǎo)出一個(gè)工廠函式(Factory function),然后呼叫并回傳一個(gè)物件。

在使用Express.js的時(shí)候便是這種作法

  1. var express = require('express') 
  2. var app = express() 
  3.  
  4. app.get('/hello'function(req,res,next){ 
  5.  res.send('Hi there!We are using Express v' + express.version) 
  6. })  

Express導(dǎo)出該函式,讓我們可以用來建立一個(gè)新的express應(yīng)用程序。

在使用這種模式時(shí),通常我們會(huì)使用factory function搭配參數(shù)讓我們可以設(shè)定并回傳初始化后的物件。

想要導(dǎo)出function,我們就一定要使用module.exports,Express便是這么做 

  1. exports = module.exports = createApplication  
  2. … 
  3. function createApplication(){ 
  4. … 
  5.  

上面指派了createApplication函式到module.exports然后再指給exports確保參考一致。

同時(shí)Express也使用下面這種方式將導(dǎo)出函式當(dāng)作命名空間的作法使用。 

  1. exports.version = '3.1.1' 

這邊要大略解釋一下由于Javascript原生并沒有支持命名空間的機(jī)制,于是大部分在JS中提到的namespace指的就是透過物件封裝的方式來達(dá)到namespace的效果,也就是***種設(shè)計(jì)模式。

注意!并沒有任何方式可以阻止我們將導(dǎo)出的函式作為命名空間物件使用,我們可以用其來引用其他的function,構(gòu)造函數(shù),物件。

Express 3.3.2 / 2013-07-03之后已經(jīng)將exports.version移除了

另外在導(dǎo)出函式的時(shí)候***為其命名,如此一來當(dāng)出錯(cuò)的時(shí)候我們比較容易從錯(cuò)誤堆疊信息中找到問題點(diǎn)。

下面是兩個(gè)簡單的例子: 

  1. /* bomb1.js */ 
  2. module.exports = function(){ 
  3.  throw new Error('boom') 
  4. module.exports = function bomb(){ 
  5.  throw new Error('boom') 
  6. $ node 
  7. > bomb = require('./bomb1'); 
  8. [Function
  9. > bomb() 
  10. Error: boom 
  11.  at module.exports(/Users/andyyou/Projects/export_this/bomb1.js:2:9) 
  12.  at repl:1:2 
  13. … 
  14. > bomb = require('./bomb2'); 
  15. [Function: bomb] 
  16. > bomb() 
  17. Error: boom  
  18.  at bomb(/Users/andyyou/Projects/export_this/bomb2.js:2:9) 
  19.  at repl:1:2 
  20. …  

導(dǎo)出函式還有些比較特別的案例,值得用另外的名稱以區(qū)分它們的不同。

導(dǎo)出高階函式

一個(gè)高階函式或functor基本上就是一個(gè)函式可以接受一個(gè)或多個(gè)函式為其輸入或輸出。而這邊我們要談?wù)摰暮笳?一個(gè)函式回傳函式

當(dāng)我們想要模塊能夠根據(jù)輸入控制回傳函式的行為時(shí),導(dǎo)出一個(gè)高階函式就是一種非常實(shí)用的設(shè)計(jì)模式。

補(bǔ)充:functor & monad

舉例來說Connect就提供了許多可掛載的功能給網(wǎng)頁框架。

這里的middleware我們先理解成一個(gè)有三個(gè)參數(shù)(req,res,next)的function。

Express從v4.x版之后不再相依于connect

connect middleware慣例就是導(dǎo)出的function執(zhí)行后,要回傳一個(gè)middleware function。

在處理request的過程中這個(gè)回傳的middleware function就可以接手使用剛剛提到的三個(gè)參數(shù),用來在過程中做一些處理或設(shè)定。

同時(shí)因?yàn)殚]包的特性這些設(shè)定在整個(gè)中間件的處理流程中都是有效的。

舉例來說,compression這個(gè)middleware就可以在處理responsive過程中協(xié)助壓縮 

  1. var connect = require('connect') 
  2. var app = connect() 
  3.  
  4. // gzip outgoing responses 
  5. var compression = require('compression') 
  6. app.use(compression())  

而它的原始碼看起來就如下 

  1. module.exports = compression 
  2. … 
  3. function compression(options){ 
  4. … 
  5.  return function compression(req,res,next){ 
  6. … 
  7.  next() 
  8.  } 
  9.  

于是每一個(gè)request都會(huì)經(jīng)過compression middleware處理,而代入的options也因?yàn)殚]包的關(guān)系會(huì)被保留下來

這是一種***彈性的模塊作法,也可能在您的開發(fā)項(xiàng)目上幫上許多忙。

middleware在這里您可以大略想成串連執(zhí)行一系列的function,自然其Function Signature要一致

導(dǎo)出構(gòu)造函數(shù)

在一般面向?qū)ο笳Z言中,constructor構(gòu)造函數(shù)指的是一小段代碼協(xié)助我們從類別Class建立一個(gè)物件。 

  1. // C# 
  2. class Car { 
  3.  // c#構(gòu)造函數(shù) 
  4.  // constructor即class中用來初始化物件的method。 
  5.  public Car(name){ 
  6.  name = name; 
  7.  } 
  8. var car = new Car('BMW');  

由于在ES2015之前Javascript并不支持類別,某種程度上在Javascript之中我們可以把任何一個(gè)function當(dāng)作類別,或者說一個(gè)function可以當(dāng)作function執(zhí)行或者搭配new關(guān)鍵字當(dāng)作constructor來使用。如果想知道更詳細(xì)的介紹可以閱讀MDN教學(xué)。

欲導(dǎo)出構(gòu)造函數(shù),我們需要透過構(gòu)造函式來定義類別,然后透過new來建立物件實(shí)例。 

  1. function Person(name){ 
  2.  this.name = name 
  3.  
  4. Person.prototype.greet = function(){ 
  5.  return 'Hi,I am ' + this.name 
  6.  
  7. var person = new Person('andyyou') 
  8. console.log(person.greet())// Hi,I am andyyou  

在這種設(shè)計(jì)模式底下,我們通常會(huì)將每個(gè)檔案設(shè)計(jì)成一個(gè)類別,然后導(dǎo)出構(gòu)造函數(shù)。這使得我們的項(xiàng)目構(gòu)架更加清楚。 

  1. var Person = require('./person') 
  2. var person = new Person()  

整個(gè)檔案看起來會(huì)如下 

  1. /* person.js */ 
  2. function Person(name){ 
  3.  this.name = name 
  4.  
  5. Person.prototype.greet = function(){ 
  6.  return 'Hi,I am ' + this.name 
  7.  
  8. exports = module.exports = Person  

導(dǎo)出單一物件實(shí)例Signleton

當(dāng)我們需要所有的模塊使用者共享物件的狀態(tài)與行為時(shí),就需要導(dǎo)出單一物件實(shí)例。

Mongoose是一個(gè)ODM(Object-Document Mapper)函式庫,讓我們可以使用程序中的Model物件去操作MongoDB。 

  1. var mongoose = require('mongoose') 
  2. mongoose.connect'mongodb://localhost/test') 
  3.  
  4. var Cat = mongoose.model('Cat',{name: String}) 
  5.  
  6. var kitty = new Cat({name'Zildjian'}) 
  7. kitty.save(function(err){ 
  8.  if(err) 
  9.  throw Error('save failed') 
  10.  console.log('meow') 
  11. }) 

 那我們r(jià)equire取得的mongoose物件是什么東西呢?事實(shí)上mongoose模塊的內(nèi)部是這么處理的 

  1. function Mongoose(){ 
  2. … 
  3.  
  4. module.exports = exports = new Mongoose()  

因?yàn)閞equire的緩存了module.exports的值,于是所有reqire('mongoose')將會(huì)回傳相同的物件實(shí)例,之后在整個(gè)應(yīng)用程序之中使用的都會(huì)是同一個(gè)物件。

Mongoose使用面向?qū)ο蟮脑O(shè)計(jì)模式來封裝,解耦(分離功能之間的相依性),維護(hù)狀態(tài)使整體具備可讀性,同時(shí)透過導(dǎo)出一個(gè)Mongoose Class的物件給使用者,讓我們可以簡單的存取使用。

如果我們有需要,它也可以建立其他的物件實(shí)例來作為命名空間使用。實(shí)際上Mongoose內(nèi)部提供了存取構(gòu)造函數(shù)的方法 

  1. Mongoose.prototype.Mongoose = Mongoose 

因此我們可以這么做 

  1. var mongoose = require('mongoose'
  2.  
  3. var Mongoose = mongoose.Mongoose 
  4.  
  5. var anotherMongoose = new Mongoose() 
  6.  
  7. anotherMongoose.connect('mongodb://localhost/test' 

擴(kuò)展全局物件

一個(gè)被匯入的模塊不只限于單純?nèi)〉闷鋵?dǎo)出的數(shù)據(jù)。它也可以用來修改全局物件或回傳全局物件,自然也能定義新的全局物件。而在這邊的全局物件(Global objects)或稱為標(biāo)準(zhǔn)內(nèi)置物件像是Object,F(xiàn)unction,Array指的是在全局能存取到的物件們,而不是當(dāng)Javascript開始執(zhí)行時(shí)所產(chǎn)生代表global scope的global object。

當(dāng)我們需要擴(kuò)增或修改全局物件預(yù)設(shè)行為時(shí)就需要使用這種設(shè)計(jì)模式。當(dāng)然這樣的方式是有爭議,您必須謹(jǐn)慎使用,特別是在開放原始碼的項(xiàng)目上。

例如:Should.js是一個(gè)常被用在單元測試中用來判斷分析值是否正確的函式庫。 

  1. require('should') 
  2.  
  3. var user = { 
  4.  name'andyyou' 
  5.  
  6. user.name.should.equal('andyyou')  

這樣您是否比較清楚了,should.js增加了底層的Object的功能,加入了一個(gè)非列舉型的屬性 should,讓我們可以用簡潔的語法來撰寫單元測試。

而在內(nèi)部should.js做了這樣的事情 

  1. var should = function(obj){ 
  2.  return new Assertion(util.isWrapperType(obj)?obj.valueOf():obj) 
  3. … 
  4. exports = module.exports = should 
  5.  
  6. Object.defineProperty(Object.prototype,'should',{ 
  7.  setfunction(){}, 
  8.  get: function(){ 
  9.  return should(this); 
  10.  }, 
  11.  configurable: true 
  12. });  

就算看到這邊您肯定跟我一樣有滿滿的疑惑,全局物件擴(kuò)展定義跟exprots有啥關(guān)聯(lián)呢?

事實(shí)上 

  1. /* whoami.js */ 
  2. exports = module.exports = { 
  3.  name'andyyou' 
  4.  
  5. Object.defineProperty(Object.prototype,'whoami',{ 
  6.  setfunction(){}, 
  7.  get: function(){ 
  8.  return 'I am ' + this.name 
  9.  } 
  10. }) 
  11.  
  12. /* app.js */ 
  13. var whoami = require('whoami') 
  14. console.log(whoami)// { name'andyyou' } 
  15.  
  16. var obj = { name'lena' } 
  17. console.log(obj.whoami)// I am lena  

現(xiàn)在我們明白了上面說的修改全局物件的意思了。should.js導(dǎo)出了一個(gè)should函式但是它主要的使用方式則是把should加到Object屬性上,透過物件本身來呼叫。

套用猴子補(bǔ)丁(Monkey Patch)

在這邊所謂的猴子補(bǔ)丁特別指的是在執(zhí)行時(shí)期動(dòng)態(tài)修改一個(gè)類別或者模塊,通常會(huì)這么做是希望補(bǔ)強(qiáng)某的第三方套件的bug或功能。

假設(shè)某個(gè)模塊沒有提供您客制化功能的界面,而您又需要這個(gè)功能的時(shí)候,我們就會(huì)實(shí)作一個(gè)模塊來補(bǔ)強(qiáng)既有的模塊。

這個(gè)設(shè)計(jì)模式有點(diǎn)類似擴(kuò)展全局物件,但并非修改全局物件,而是依靠Node模塊系統(tǒng)的緩存機(jī)制,當(dāng)其他代碼匯入該模塊時(shí)去補(bǔ)強(qiáng)該模塊的實(shí)例物件。

預(yù)設(shè)來說Mongoose會(huì)使用小寫以及復(fù)數(shù)的慣例替數(shù)據(jù)模型命名。例如一個(gè)數(shù)據(jù)模型叫做CreditCard最終我們會(huì)得到collection的名稱是creditcards。假如我們希望可以換成credit_cards并且其他地方也遵循一樣的用法。

下面是我們試著使用猴子補(bǔ)丁的方式來替既有的模塊增加功能 

  1. var pluralize = require('pluralize')//處理復(fù)數(shù)單字的函式庫 
  2. var mongoose = require('mongoose') 
  3. var Mongoose = mongoose.Mongoose 
  4.  
  5. mongoose.Promise = global.Promise // v4.1+ http://mongoosejs.com/docs/promises.html 
  6. var model = Mongoose.prototype.model 
  7.  
  8. //補(bǔ)丁 
  9. var fn = functionnameschema,collection,skipInit){ 
  10.  collection = collection || pluralize.plural(name.replace(/([a-z\d])([A-Z])/g,'$1_$2').toLowerCase()) 
  11.  return model.call(this,name,schema,collection,skipInit) 
  12. Mongoose.prototype.model = fn 
  13.  
  14. /*實(shí)際測試*/ 
  15. mongoose.connect'mongodb://localhost/test') 
  16. var CreditCardSchema = new mongoose.Schema({number: String}) 
  17. var CreditCardModel = mongoose.model('CreditCard',CreditCardSchema); 
  18.  
  19. var card = new CreditCardModel({number: '5555444433332222'}); 
  20. card.save(function(err){ 
  21.  if(err){ 
  22.  console.log(err) 
  23.  } 
  24.  console.log('success') 
  25. })  

您不該輕易使用上面這種方式補(bǔ)丁,這邊只是為了說明猴子補(bǔ)丁這種方式,mongoose已經(jīng)有提供官方的方式設(shè)定名稱 

  1. var schema = new Schema({..},{ collection: 'your_collection_name' }) 

當(dāng)這個(gè)模塊***次被匯入的時(shí)候便會(huì)讓mongoose重新定義Mongoose.prototype.model并將其設(shè)回原本的model的實(shí)作。

如此一來所有Mongoose的實(shí)例物件都具備新的行為了。注意到這邊并沒有修改exports所以當(dāng)我們r(jià)equire的時(shí)候得到的是預(yù)設(shè)的物件

另外如果您想使用上面這種補(bǔ)丁的方式時(shí),記得閱讀原始碼并注意是否產(chǎn)生沖突。

請善用導(dǎo)出的功能

Node模塊系統(tǒng)提供了一個(gè)簡單的機(jī)制來封裝功能,使我們能夠建立了清楚的界面。希望掌握這七種設(shè)計(jì)模式提供不同的優(yōu)缺點(diǎn)能對您有所幫助。

在這邊作者并沒有徹底的調(diào)查所有的方式,一定有其他選項(xiàng)可供選擇,這邊只有描述幾個(gè)最常見且不錯(cuò)的方法。

小結(jié)

  • namespace:導(dǎo)出一個(gè)物件包含需要的功能

root module的方式,使用一個(gè)根模塊導(dǎo)出其他模塊

  • function:直接將module.exports設(shè)為function

Function物件也可以拿來當(dāng)作命名空間使用

為其命名方便調(diào)試

exports = module.exports = something的作法是為了確保參考(Reference)一致

  • high-order function:可以透過代入?yún)?shù)控制并回傳function。

可協(xié)助實(shí)作middleware的設(shè)計(jì)模式

換句話說middleware即一系列相同signature的function串連。一個(gè)接一個(gè)執(zhí)行

  • constructor:導(dǎo)出類別(function),使用時(shí)再new,具備OOP的優(yōu)點(diǎn)
  • singleton:導(dǎo)出單一物件實(shí)例,重點(diǎn)在各個(gè)檔案可以共享物件狀態(tài)
  • global objects:在全局物件作的修改也會(huì)一起被導(dǎo)出
  • monkey patch:執(zhí)行時(shí)期,利用Node緩存機(jī)制在instance加上補(bǔ)丁

筆記

  • 一個(gè)javascript檔案可視為一個(gè)模塊
  • 解決特定問題或需求,功能完整由單一或多個(gè)模塊組合而成的整體稱為套件(package)
  • require匯入的模塊具有自己的scope
  • exports只是module.exports的參考,exports會(huì)記錄收集屬性如果module.exports沒有任何屬性就把其數(shù)據(jù)交給module.exports,但如果module.exports已經(jīng)具備屬性的話,那么exports的所有數(shù)據(jù)都會(huì)被忽略。
  • 就算exports置于后方仍會(huì)被忽略
  • Node初始化的順序

Native Module -> Module

StartNodeInstance()-> CreateEnvironment()-> LoadEnvironment()-> Cached

  • Native Module加載機(jī)制

檢查是否有緩存

->有;直接回傳this.exports

->沒有;new一個(gè)模塊物件

cache()

compile()-> NativeModule.wrap()將原始碼包進(jìn)function字串->runInThisContext()建立函式

return NativeModule.exports

  • Node的require會(huì)cache,也就是說:如果希望模塊產(chǎn)生不同的instance時(shí)應(yīng)使用function
責(zé)任編輯:龐桂玉 來源: segmentfault
相關(guān)推薦

2021-02-19 14:07:03

JavaScript編程開發(fā)

2009-06-29 18:11:40

JSP設(shè)計(jì)模式

2009-01-04 13:49:17

Java設(shè)計(jì)模式設(shè)計(jì)模式工廠模式

2023-09-22 11:58:49

2020-10-14 13:58:14

23種設(shè)計(jì)模式速記

2020-10-09 06:52:31

設(shè)計(jì)模式軟件

2015-09-14 09:31:44

結(jié)對設(shè)計(jì)

2009-06-15 14:15:07

Java設(shè)計(jì)模式Java

2022-05-27 11:33:02

前端代碼設(shè)計(jì)模式

2023-05-15 15:29:13

設(shè)計(jì)模式JavaScript

2012-08-30 09:07:33

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

2021-04-09 20:38:20

Vue模式.前端

2018-08-29 10:04:43

2021-04-18 21:07:32

門面模式設(shè)計(jì)

2024-07-31 08:12:33

2023-10-26 09:02:30

框架設(shè)計(jì)模式

2012-04-10 10:04:26

并行編程

2021-03-03 16:01:48

Web設(shè)計(jì)模式

2020-04-23 11:03:09

前端語言開發(fā)

2012-05-28 09:16:12

Java設(shè)計(jì)模式
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)