從module的簡單實現到模塊化

前言

一直感受模塊化是一個很神祕的概念,於是也很感興趣,有幸瞭解到了模塊化的歷史,嘗試着瞭解了一下模塊化的實現,發現了一個頗有意思的東西,不知道爲何,會以爲頗有成就感,記錄一下。javascript

模塊化的實現

簡單模擬一下CMD的module實現,在module.js中作如下處理java

//module.js
exports.word = 'hello'
module.exports = function () {
  console.log(exports.word)
}
複製代碼

而後在test.js界面引入該模塊es6

//test.js
const file = require('./module.js')
file() // hello
console.log(file.word) //undefined
複製代碼

不考慮爲何會有這樣的結果,上面的這部分是經過require引用模塊的常見寫法。下面的超簡化版本代碼實現了超簡化的require方法(下面的runner方法)閉包

//require.js
const fs = require('fs')
const path = require('path')

function runner(file) {
  const code = fs.readFileSync(path.join(__dirname, file), 'utf-8')
  const module = { exports: {} }
  const fn = new Function('module', 'exports', code)
  fn(module, module.exports)
  return module.exports
}
複製代碼

這裏的fn整理出來就是下面這一段代碼模塊化

function _fn(module, exports) {
  exports.word = 'hello'
  module.exports = function() {
    console.log(exports.word)
  }
}
複製代碼

因此在執行fn(module, module.exports)時,就是對上面聲明的const module = { exports: {} }進行賦值。當執行runner方法時,其實就是獲取module.exports的值。而runner方法作的事,就是獲取文件中的內容,識別module.exports,並把該值拋出來。轉到require,其實主要作的也就是這部分工做。module和 export也就成了關鍵詞,用來在讀取模塊的時候識別的標識。函數

runner方法已經實現了,如今來看一下爲何運行後是這樣的結果。ui

export 與 module.export

經過上面runner的實現,也能夠看出,其實exportsmodule.exports最後被引用後實際上是一個。最後暴露出來的都是module對象,而若是一個頁面中同時存在exportsmodule.exports,最後一個引用都會覆蓋掉掉前面全部的exports引用,因此這也是爲何上面的file()能執行成功,而file.wordundefined,由於前者覆蓋了後者。file已經被替換成了下面這個方法spa

function() {
    console.log(exports.word)
}
複製代碼

模塊化

那麼什麼叫模塊化呢?個人理解有3點:code

  1. 引用的時候能按需調用
  2. 外部調用沒法修改內部參數
  3. 沒有全局變量的污染,方法名不衝突

由於看了模塊化的歷史,瞭解到模塊化最最原始是從匿名閉包衍生出來的,再看看如今的module的實現,其實也是一個閉包。雖然以前看了不少閉包的概念,但由於使用上的侷限性,一直把閉包誤解爲只有return出去的纔是閉包。好比下面這段代碼對象

function fo(){
    var a = 'aaa';
    return function(){
        console.log(a)
    }
}
var bar = fo();
複製代碼

此次看到這個是真的有刷新個人認知,原來下面這樣的也是閉包

var b = {
    a: {}
}
function fo(obj){
    let a = 'aaa';
    obj.a = function(){
        console.log(a)
    }
}
fo(b.a);
複製代碼

把上面這個再擴展一下就成了下面這段代碼,一段相似_fn的代碼

var b = {
    a: {}
}
function fo(b, a){
    a = '111';
    b.a = function(){
        console.log(a)
    }
}
fo(b, b.a);
複製代碼

重複一波閉包的概念:可以讀取其餘函數內部變量的函數

因此這也是爲何上面的file()的結果是hello,這也是requireJS的模塊化裏面我暫時接觸到的最有意思的地方。下次瞅一瞅es6的模塊化

相關知識

模塊化的歷史:huangxuan.me/js-module-7…

相關文章
相關標籤/搜索