成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

[譯 + 更新] 參透 Node 中 exports 的 7 種設(shè)計(jì)模式

wmui / 3381人閱讀

摘要:現(xiàn)在,我們可以開始探討介面的設(shè)計(jì)模式了。匯出命名空間一個(gè)簡(jiǎn)單且常用的設(shè)計(jì)模式就是匯出一個(gè)包含數(shù)個(gè)屬性的物件,這些屬性具體的內(nèi)容主要是函式,但並不限於函式。如此,我們就能夠透過匯入該模組來取得這個(gè)命名空間下一系列相關(guān)的功能。

前言

這篇文章試著要整理,翻譯Export This: Interface Design Patterns for Node.js Modules這篇非常值得一讀的文章。
但因?yàn)檫@篇文章有些時(shí)日了,部分範(fàn)例已經(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)

匯出命名空間 Namespace

匯出函式 Function

匯出高階函式 High-Order Function

匯出建構(gòu)子/構(gòu)建函式 Constructor

匯出單一實(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 文件

每個(gè)模組內(nèi)都有一個(gè)隱式(implicit)的 module 物件,這個(gè)物件身上有一個(gè)屬性 exports (即 module.exports)。當(dāng)我們使用 require() 時(shí)它所傳回的,即是這個(gè)模組的 module.exports 所指向的東西。
每個(gè)模組的 module.exports 預(yù)設(shè)都是一個(gè)空物件 {},而每個(gè)模組內(nèi)帶有一個(gè) exports 捷徑變數(shù),預(yù)設(shè)指向 module.exports

預(yù)設(shè)情況下,module.exports 是一個(gè)物件,而 exports 又指向該物件,所以在預(yù)設(shè)情況下,以下兩個(gè)操作視為等效:

exports.foo = "hello";
// 等同於
module.exports.foo = "hello";

如果,你重新指定了某東西給 module.exports,當(dāng)然 module.exports 跟預(yù)設(shè)物件的關(guān)係就因此被打斷,而此時(shí) exports 捷徑變數(shù)仍然是指向預(yù)設(shè)物件的。前面有提到,當(dāng)我們使用 require() 時(shí),模組是以 module.exports 所指向的東西而揭露給外部,因此模組的介面是由 module.exports 所決定,與 exports 捷徑變數(shù)無關(guān)。
因此,一旦 module.exports 被指定新值後,使用捷徑變數(shù) exports 所掛入預(yù)設(shè)物件的內(nèi)容都將無法被揭露,因而可視為無效。

為了讓您更好理解關(guān)於 exportsmodule.exports 下面的範(fàn)例提供了較詳細(xì)的說明

var a = { id: 1 }
var b = a
console.log(a) // {id: 1}
console.log(b) // {id: 1}

// b 參考指向 a,意味著修改 b 的屬性 a 會(huì)跟著變動(dòng)
b.id = 2
console.log(a) // {id: 2}
console.log(b) // {id: 2}

// 但如果將一個(gè)全新的物件賦予 b 那麼參考的關(guān)係將會(huì)中斷
b = { id: 3 }
console.log(a) // {id: 2}
console.log(b) // {id: 3}

另外比較具體的範(fàn)例

// 模組預(yù)設(shè): module.exports = {}, 而 exports = module.exports, 等同於指向預(yù)設(shè)的空物件

/* person.js */
exports.name = function () {
  console.log("My name is andyyou.")
}
...
/* main.js */
var person = require("./person.js")
person.name()
/* person.js */
module.exports = "Hey, andyyou"        // module.exports 不再是預(yù)設(shè)物件
exports.name = function () {        // 透過 exports 捷徑掛進(jìn)預(yù)設(shè)物件的內(nèi)容, 將無法被揭露
  console.log("My name is andyyou")
}

/* main.js */
var person = require("./person.js")
// exports 的屬性被忽略了
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 的資料都會(huì)被忽略。

本質(zhì)上我們可以理解為所有模組都隱含實(shí)作了下面這行程式碼 (實(shí)際上 moduleexports 是由 node 模組系統(tǒng)傳入給我們的模組的)

var exports = module.exports = {}

現(xiàn)在我們知道了,當(dāng)我們要匯出一個(gè) function 時(shí)我們得使用 module.exports
如果令捷徑 exports 變數(shù)指向該 function,僅僅是捷徑 exports 對(duì) module.exports 的關(guān)係被打斷而已,真正揭露的還是 module.exports。

另外,我們?cè)谠S多專案看到下面的這行程式碼

exports = module.exports = something

這行程式碼作用就是確保 exportsmodule.exports 被我們覆寫之後,仍可以指向相同的參考。

接著我們就可以透過 module.exports 來定義並匯出一個(gè) function

/* function.js */
module.exports = function () {
  return { name: "andyyou" }
}

使用的方式則是

var func = require("./function")

關(guān)於 require 一個(gè)很重要的行為就是它會(huì)快取(Cache) module.exports 的值,未來每一次 require 被調(diào)用時(shí)都會(huì)回傳相同的值。
它會(huì)根據(jù)匯入檔案的絕對(duì)路徑來快取,所以當(dāng)我們想要模組能夠回傳不同得值時(shí),我們就需要匯出 function,如此一來每次執(zhí)行函式時(shí)就會(huì)回傳一個(gè)新值。

下面在 Node REPL 中簡(jiǎn)易的示範(fàn)

$ node
> f1 = require("/Users/andyyou/Projects/export_this/function")
[Function]
> f2 = require("./function") // 相同路徑
[Function]
> f1 === f2
true
> f1() === f2()
false

您可以觀察到 require 回傳了同樣的函式物件實(shí)例,但每一次調(diào)用函式回傳的物件是不同的。
更詳細(xì)的介紹可以參考官方文件,值得一讀。

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

匯出命名空間

一個(gè)簡(jiǎn)單且常用的設(shè)計(jì)模式就是匯出一個(gè)包含數(shù)個(gè)屬性的物件,這些屬性具體的內(nèi)容主要是函式,但並不限於函式。
如此,我們就能夠透過匯入該模組來取得這個(gè)命名空間下一系列相關(guān)的功能。

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

var fs = require("fs")
var readFile = fs.readFile
var ReadStream = fs.ReadStream

readFile("./file.txt", function (err, data) {
  console.log("readFile contents: %s", data)
})

這便是fs 核心模組的做法

var fs = exports

首先用一個(gè)新的區(qū)域變數(shù) fs,令其為捷徑 exports,因此預(yù)設(shè)情況下 fs 就指向了 module.exports 身上的預(yù)設(shè)物件。上面這一行我們可以說,只是將捷徑變數(shù)換個(gè)名稱成為 fs 如此而已。
接下來,我們就可以使用新的捷徑 fs 了,例如: fs.Stats = binding.Stats

fs.readFile = function (path, options, callback_) {
  // ...
}

其他東西也是一樣的作法,例如匯出建構(gòu)子

fs.ReadStream = ReadStream
function ReadStream(path, options) {
  // ...
}
ReadStream.prototype.open = function () {
  // ...
}

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

/* exports 作法 */
exports.verstion = "1.0"

/* 或者 module.exports 作法 */
module.exports = {
  version: "1.0",
  doYourTasks: function () {
    // ...
  }
}

一個(gè)常見的作法就是透過一個(gè)根模組(root)來彙整並匯出其他模組,如此一來只需要一個(gè) require 便可以使用所有的模組。
原文作者在Good Eggs工作時(shí),會(huì)將資料模型(Model)拆分成個(gè)別的模組,並使用匯出建構(gòu)子的方式匯出(請(qǐng)參考下文介紹),然後透過一個(gè) index 檔案 來集合該目錄下所有的資料模型並一起匯出,如此一來在 models 命名空間下的所有資料模型都可以使用

var models = require("./models")
var User = models.User
var Product = models.Product

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

/* CoffeeScript */
{User, Product} = require "./models"

/* ES2015 */
import {User, Product} from "./models"

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

exports.User = require("./User")
exports.Person = require("./person")

實(shí)際上這樣分開的寫法還有更精簡(jiǎn)的寫法,我們可以透過一個(gè)小小的函式庫(kù)來匯入在同一階層中所有檔案並搭配 CamelCase 的命名規(guī)則匯出。
於是在我們的 index.js 中看起來就會(huì)如下

module.exports = require("../lib/require_siblings")(__filename)
匯出函式

另外一個(gè)設(shè)計(jì)模式是匯出函式當(dāng)作該模組的介面。常見的作法是匯出一個(gè)工廠函式(Factory function),然後呼叫並回傳一個(gè)物件。
在使用 Express.js 的時(shí)候便是這種作法

var express = require("express")
var app = express()    // 實(shí)際上 express() 傳回的 app 是一個(gè) function,在 JS 中函式也是物件

app.get("/hello", function (req, res, next) {
  res.send("Hi there! We are using Express v" + express.version)
})

Express 匯出該函式,讓我們可以用來建立一個(gè)新的 express 應(yīng)用程式。
在使用這種模式時(shí),通常我們會(huì)使用 factory function 搭配參數(shù)讓我們可以設(shè)定並回傳初始化後的物件。

想要匯出 function,我們就一定要使用 module.exports ,Express 便是這麼做

exports = module.exports = createApplication

...
function createApplication () {
  ...
}

上面指派了 createApplication 函式到 module.exports 然後再指給 exports 確保參考一致。
同時(shí) Express 也使用下面這種方式將導(dǎo)出函式當(dāng)作命名空間的作法使用。

exports.version = "3.1.1"

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

注意!並沒有任何方式可以阻止我們將匯出的函式作為命名空間物件使用,我們可以用其來引用其他的 function,建構(gòu)子,物件。

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

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

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

/* bomb1.js */
module.exports = function () {
  throw new Error("boom")
}
module.exports = function bomb() {
  throw new Error("boom")
}
$ node
> bomb = require("./bomb1");
[Function]
> bomb()
Error: boom
    at module.exports (/Users/andyyou/Projects/export_this/bomb1.js:2:9)
    at repl:1:2
    ...
> bomb = require("./bomb2");
[Function: bomb]
> bomb()
Error: boom
    at bomb (/Users/andyyou/Projects/export_this/bomb2.js:2:9)
    at repl:1:2
    ...

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

匯出高階函式

一個(gè)高階函式或 functor 基本上就是一個(gè)函式可以接受一個(gè)或多個(gè)函式為其輸入或輸出。而這邊我們要談?wù)摰尼嵴?- 一個(gè)函式回傳函式
當(dāng)我們想要模組能夠根據(jù)輸入控制回傳函式的行為時(shí),匯出一個(gè)高階函式就是一種非常實(shí)用的設(shè)計(jì)模式。

補(bǔ)充:functor & monad

舉例來說 Connect 就提供了許多可掛載的功能給網(wǎng)頁框架。
這裡的 middleware 我們先理解成一個(gè)有三個(gè)參數(shù) (req, res, next) 的 function。

Express 從 v4.x 版之後不再相依於 connect

connect middleware 慣例就是匯出的 function 執(zhí)行後,要回傳一個(gè) middleware function
在處理 request 的過程中這個(gè)回傳的 middleware function 就可以接手使用剛剛提到的三個(gè)參數(shù),用來在過程中做一些處理或設(shè)定。
同時(shí)因?yàn)殚]包的特性這些設(shè)定在整個(gè)中介軟體的處理流程中都是有效的。

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

var connect = require("connect")
var app = connect()

// gzip outgoing responses
var compression = require("compression")
app.use(compression())

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

module.exports = compression
...
function compression (options) {
  ...
  return function compression (req, res, next) {
    ...
    next()
  }
}

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

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

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

匯出建構(gòu)子

在一般物件導(dǎo)向語言中,constructor 建構(gòu)子指的是協(xié)助我們從類別 Class建立一個(gè)該類別物件實(shí)例的一段程式碼。

// C#
class Car {
  // c# 建構(gòu)子
  // constructor 即 class 中用來初始化物件的 method。
  public Car(name) {
    name = name;
  }
}
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é)。

欲匯出建構(gòu)子,我們需要透過構(gòu)造函式來定義類別,然後透過 new 來建立物件實(shí)例。

function Person (name) {
  this.name = name
}

Person.prototype.greet = function () {
  return "Hi, I am " + this.name
}

var person = new Person("andyyou")
console.log(person.greet()) // Hi, I am andyyou

在這種設(shè)計(jì)模式底下,我們通常會(huì)將每個(gè)檔案設(shè)計(jì)成一個(gè)類別,然後匯出建構(gòu)子。這使得我們的專案架構(gòu)更加清楚。

var Person = require("./person")
var person = new Person()

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

/* person.js */
function Person(name) {
  this.name = name
}

Person.prototype.greet = function () {
  return "Hi, I am " + this.name
}

exports = module.exports = Person
匯出單一物件實(shí)例 Signleton

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

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

var mongoose = require("mongoose")
mongoose.connect("mongodb://localhost/test")

var Cat = mongoose.model("Cat", {name: String})

var kitty = new Cat({name: "Zildjian"})
kitty.save(function (err) {
  if (err)
    throw Error("save failed")
  console.log("meow")
})

那我們 require 取得的 mongoose 物件是什麼東西呢?事實(shí)上 mongoose 模組的內(nèi)部是這麼處理的

function Mongoose() {
  ...
}

module.exports = exports = new Mongoose()

因?yàn)?require 的快取了 module.exports 的值,於是所有 reqire("mongoose") 將會(huì)回傳相同的物件實(shí)例,之後在整個(gè)應(yīng)用程式之中使用的都會(huì)是同一個(gè)物件。

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

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

Mongoose.prototype.Mongoose = Mongoose

因此我們可以這麼做

var mongoose = require("mongoose")
var Mongoose = mongoose.Mongoose

var anotherMongoose = new Mongoose()
anotherMongoose.connect("mongodb://localhost/test")
擴(kuò)展全域物件

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

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

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

require("should")

var user = {
  name: "andyyou"
}

user.name.should.equal("andyyou")

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

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

var should = function (obj) {
  return new Assertion(util.isWrapperType(obj) ? obj.valueOf(): obj)
}
...
exports = module.exports = should

Object.defineProperty(Object.prototype, "should", {
  set: function(){},
  get: function(){
    return should(this);
  },
  configurable: true
});

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

事實(shí)上

/* whoami.js */
exports = module.exports = {
  name: "andyyou"
}

Object.defineProperty(Object.prototype, "whoami", {
  set: function () {},
  get: function () {
    return "I am " + this.name
  }
})

/* app.js */
var whoami = require("whoami")
console.log(whoami) // { name: "andyyou" }

var obj = { name: "lena" }
console.log(obj.whoami) // I am lena

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

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

在這邊所謂的猴子補(bǔ)丁特別指的是在執(zhí)行時(shí)期動(dòng)態(tài)修改一個(gè)類別、模組或物件(也稱 object augmentation),通常會(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ì)使用小寫以及複數(shù)的慣例替資料模型命名。例如一個(gè)資料模型叫做 CreditCard 最終我們會(huì)得到 collection 的名稱是 creditcards 。假如我們希望可以換成 credit_cards 並且其他地方也遵循一樣的用法。

下面是我們?cè)囍褂煤镒友a(bǔ)丁的方式來替既有的模組增加功能

var pluralize = require("pluralize") // 處理複數(shù)單字的函式庫(kù)
var mongoose = require("mongoose")
var Mongoose = mongoose.Mongoose

mongoose.Promise = global.Promise // v4.1+ http://mongoosejs.com/docs/promises.html
var model = Mongoose.prototype.model

// 補(bǔ)丁
var fn = function(name, schema, collection, skipInit) {
  collection = collection || pluralize.plural(name.replace(/([a-zd])([A-Z])/g, "$1_$2").toLowerCase())
  return model.call(this, name, schema, collection, skipInit)
}
Mongoose.prototype.model = fn

/* 實(shí)際測(cè)試 */
mongoose.connect("mongodb://localhost/test")
var CreditCardSchema = new mongoose.Schema({number: String})
var CreditCardModel = mongoose.model("CreditCard", CreditCardSchema);

var card = new CreditCardModel({number: "5555444433332222"});
card.save(function (err) {
  if (err) {
    console.log(err)
  }
  console.log("success")
})

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

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)我們 require 的時(shí)候得到的是預(yù)設(shè)的物件

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

請(qǐng)善用匯出的功能

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

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

小結(jié)

namespace: 匯出一個(gè)物件包含需要的功能

root module 的方式,使用一個(gè)根模組匯出其他模組

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

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

為其命名方便偵錯(cuò)

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

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

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

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

constructor: 匯出類別(function),使用時(shí)再 new,具備 OOP 的優(yōu)點(diǎn)

singleton: 匯出單一物件實(shí)例,重點(diǎn)在各個(gè)檔案可以共享物件狀態(tài)

global objects: 在全域物件作的修改也會(huì)一起被匯出

monkey patch: 執(zhí)行時(shí)期,利用 Node 快取機(jī)制在 instance 加上補(bǔ)丁

筆記

一個(gè) javascript 檔案可視為一個(gè)模組

解決特定問題或需求,功能完整由單一或多個(gè)模組組合而成的整體稱為套件(package)

require 匯入的模組具有自己的 scope

exports 只是 module.exports 的參考,exports 會(huì)記錄收集屬性如果 module.exports 沒有任何屬性就把其資料交給 module.exports ,但如果 module.exports 已經(jīng)具備屬性的話,那麼exports 的所有資料都會(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

資源

官方 module 文件

理解 module exports

Export This: Interface Design Patterns for Node.js Modules

module.exports v.s exports

從 node.js 原始碼看 exports 與 module.exports

Export This 中文

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/80417.html

相關(guān)文章

  • [] 透過重新實(shí)作來學(xué)習(xí)參透閉包

    摘要:不過到底是怎麼保留的另外為什麼一個(gè)閉包可以一直使用區(qū)域變數(shù),即便這些變數(shù)在該內(nèi)已經(jīng)不存在了為了解開閉包的神秘面紗,我們將要假裝沒有閉包這東西而且也不能夠用嵌套來重新實(shí)作閉包。 原文出處: 連結(jié) 話說網(wǎng)路上有很多文章在探討閉包(Closures)時(shí)大多都是簡(jiǎn)單的帶過。大多的都將閉包的定義濃縮成一句簡(jiǎn)單的解釋,那就是一個(gè)閉包是一個(gè)函數(shù)能夠保留其建立時(shí)的執(zhí)行環(huán)境。不過到底是怎麼保留的? 另外...

    CoXie 評(píng)論0 收藏0
  • 手把手深入理解 webpack dev middleware 原理與相關(guān) plugins

    摘要:的架構(gòu)設(shè)計(jì)促使第三方開發(fā)者讓核心發(fā)揮出無限的潛力。當(dāng)然建置比起開發(fā)是較進(jìn)階的議題,因?yàn)槲覀儽仨氁斫鈨?nèi)部的一些事件。這個(gè)編譯結(jié)果包含的訊息包含模組的狀態(tài),編譯後的資源檔,發(fā)生異動(dòng)的檔案,被觀察的相依套件等。 本文將對(duì) webpack 周邊的 middleware 與 plugin 套件等作些介紹,若您對(duì)於 webpack 還不了解可以參考這篇彙整的翻譯。 webpack dev ser...

    gitmilk 評(píng)論0 收藏0
  • [] scroll-behavior 滑順捲動(dòng)效果

    摘要:不過這個(gè)效果感覺上就像是閃一下就切換到該位置。為了使用體驗(yàn)上的感覺有時(shí)候網(wǎng)站會(huì)設(shè)計(jì)一種平滑捲動(dòng)到該位置的效果。的方式非常簡(jiǎn)單,只要在該元素設(shè)定注意是而不是這個(gè)方式非常方便不過目前只有支援,查閱。 眾所皆知 HTML 錨點(diǎn)(anchor link)透過給定標(biāo)籤 id 屬性跳到頁面上特定位置的功能。不過這個(gè)效果感覺上就像是閃一下就切換到該位置。為了使用體驗(yàn)上的感覺有時(shí)候網(wǎng)站會(huì)設(shè)計(jì)一種平滑捲...

    PiscesYE 評(píng)論0 收藏0
  • [] scroll-behavior 滑順捲動(dòng)效果

    摘要:不過這個(gè)效果感覺上就像是閃一下就切換到該位置。為了使用體驗(yàn)上的感覺有時(shí)候網(wǎng)站會(huì)設(shè)計(jì)一種平滑捲動(dòng)到該位置的效果。的方式非常簡(jiǎn)單,只要在該元素設(shè)定注意是而不是這個(gè)方式非常方便不過目前只有支援,查閱。 眾所皆知 HTML 錨點(diǎn)(anchor link)透過給定標(biāo)籤 id 屬性跳到頁面上特定位置的功能。不過這個(gè)效果感覺上就像是閃一下就切換到該位置。為了使用體驗(yàn)上的感覺有時(shí)候網(wǎng)站會(huì)設(shè)計(jì)一種平滑捲...

    james 評(píng)論0 收藏0
  • [ + 補(bǔ)充] Webpack 2 入門

    摘要:目錄許多開發(fā)者會(huì)把的目錄命名為但這並不強(qiáng)迫。所有的檔案都會(huì)使用從被編譯成。同時(shí)有個(gè)小小的重點(diǎn)那就是我們可已觀察編譯後的檔案大小。在專案目錄下執(zhí)行可以觀察截至目前為止的結(jié)果。我們的目標(biāo)是要把編譯封裝到我們的中。 在今時(shí)今日,webpack 已經(jīng)成為前端開發(fā)非常重要的工具之一。本質(zhì)上它是一個(gè) Javascript 模組封裝工具,但透過 loaders 和 plugins 它也可以轉(zhuǎn)換封裝其...

    betacat 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<