亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

seajs 3.0.0 源碼閱讀筆記

發布時間:2020-10-12 19:50:27 來源:網絡 閱讀:1570 作者:邊城__ 欄目:web開發

寫筆記的時候才注意到我看的源代碼是 3.0.0 的,但是官方發布的最新版本是 2.3.0。相信大部分是相同的,所以先把這個記完,再看一次 2.3.0 的代碼。

seajs 的源代碼可以在 github上獲取。seajs 在文檔“如何參與開發”中說明了閱讀順序,當然為了便于閱讀,在了解了目錄結構之后,我直接閱讀了合并好的 sea-debug.js。

整個seajs采用的是2空格縮進,避免分號的寫法,我不是很習慣,但不影響閱讀。

文件/目錄結構

文檔中各個源文件所包含的內容大致如下:

intro.js 
文件頭

sea.js 
定義全局 global.seajs 對象和 seajs.data 對象

util-lang.js,語言相關工具 
定義用于判斷對象類型的 isXxxxx() 函數,以及一個與語言無關的 cid()

util-events.js 
定義 seajs 的事件處理相關函數,包括 on()off()emit()

util-path.js 
定義用于路徑處理的工具函數

util-request.js 
定義請求文件的工具函數 seajs.request() 等

util-deps.js 
定義用于分析依賴關系的 parseDependencies()

module.js 
seajs 的核心,模塊類。 
也包含部分 seajs 的方法

config.js 
定義 seajs.config(),以及 data 部分屬性的默認值

所以合并之后的整個 seajs 代碼看起來就像這樣

(function(global, undefiend) {
  global.seajs = {
    data: {}
  }
  var isObject = function() {}
  var isString = function() {}
  var isArray = function() {}
  var isFunction = function() {}
  var cid = function() {}
  data.events = {}
  seajs.on = function() {}
  seajs.off = function() {}
  seajs.emit = function() {}
  // path utils, and
  seajs.resolve = function() {}
  var loaderDir
  var parseDependencies = function() {}
  function Module() {}
  seajs.config = function() {}
})(this);

isXxxx 用于判斷對象類型

首先是定義了一個產生 isXxxx 的函數工廠 isType()

function isType(type) {
  return function(obj) {
    return {}.toString.call(obj) == "[object " + type + "]"
  }
}


從這個工廠的代碼可以看出來,isXxxx() 主要是通過 Object.prototype.toString 的值來判斷對象類型的。

當然也有例外:

// 畢竟 Array.isArray() 是 [native code],效果會高得多
var isArray = Array.isArray || isType("Array")


這里有幾件事情我不是很明白:

  1. 就是為什么不使用 typeof 運算符來判斷類型,一般語言中運算符實現會比比較字符串快得多。

  2. {}.toString.call(obj) 和 Object.prototype.toString.call(obj) 的作用是一樣的,但是在運行時,每執行一次 isXxxx 就會產生一個新的 {} 對象;而 Object.prototype 始終都是同一個對象,似乎可以減少不少開銷

  3. jQuery 的 isFunction() 等方法都是通過 jQuery.type() 來實現的,而 jQuery.type 中則是通過定義了一個 class2type 字典對象來做類型映射,


2014-09-09 補充

我在 seajs 的 issue 上問了這個問題,于是上面的幾個問題就都找到答案了:

https://github.com/seajs/seajs/issues/1314


參考玉伯的博客

https://github.com/lifesinger/lifesinger.github.com/issues/175

jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
    class2type[ "[object " + name + "]" ] = name.toLowerCase();
});


這種作法也會比每次都拼字符串("[object " + type + "]")來比較要效率高一些吧。

不過 seajs 和 jquery 都沒有直接依賴 typeof 運算符來實現 isXxxx,我相信絕對不是偶然,一定有啥原因,不過這個原因我目前就不清楚了,希望玉伯能看到這個博客,稍作解釋

事件

seajs 只為全局對象 seajs 添加了事件處理。事件的回調函數鏈保存在 seajs.data.events 中,以事件名稱為 key,Array 對象保存的回調函數鏈為 value。

因為 seajs 的事件主要為了插件而定義,所以對參數并沒有嚴格的校驗。比如

// 自定義一個比較奇葩的事件,這不會報錯
seajs.on(null, "fake callback");
// 但是執行就會出錯了
seajs.emit("null")
// 輸出:TypeError: string is not a function


看樣子,插件開發者得自己注意下這個問題了。

看完 seajs 的源碼,大概定義了這么一些事件

  1. error,貌似跟 NodeJS 有點關系,沒仔細看

  2. load,在模塊對象狀態變成 LOADING 后觸發,參數是所有依賴模塊的 URI。

      // Emit `load` event for plugins such as combo plugin
      var uris = mod.resolve()
      emit("load", uris)
  3. exec,在模塊對象狀態變成 EXECUTED 后觸發,參數就是模塊對象本身

  4. fetch,在模塊對象狀態剛變成 FETECHING 時觸發,參數是一個臨時對象emitData,事件結果保存在 emitData.requestUri,用于后面的 request 請求數據。

      // Emit `fetch` event for plugins such as combo plugin
      var emitData = { uri: uri }
      emit("fetch", emitData)
      var requestUri = emitData.requestUri || uri
  5. request,在 fetch 事件后對 emitData.requestUri || uri 進行了處理之后,通過 seajs.request() 請求數據之前觸發,參數是一個臨時對象,變量名復用的 emitData。事件處理完成后根據 emitData.requested 的值來判斷是否需要調用 seajs.request 請求數據。

      // Emit `request` event for plugins such as text plugin
      emit("request", emitData = {
        uri: uri,
        requestUri: requestUri,
        onRequest: onRequest,
        charset: isFunction(data.charset)
          ? data.charset(requestUri) || 'utf-8'
          : data.charset
      })
      if (!emitData.requested) {
        // ...
      }
  6. resolve,在 Module.resolve 中調用 seajs.resolve() 之前觸發,參數是一個臨時對象 emitData。事件中如果產生了有效的 emitData.uri,則不再調用seajs.resolve()

      // Emit `resolve` event for plugins such as text plugin
      var emitData = { id: id, refUri: refUri }
      emit("resolve", emitData)
      return emitData.uri || seajs.resolve(emitData.id, refUri)
  7. config,在 seajs.config() 中,完成對 config 對象的處理之后觸發,參數就是 config 對象。

    seajs.config = function(configData) {
      // ...
      emit("config", configData)
      return seajs
    }

Module 類

Module 類才是 seajs 的重頭戲,核心的核心。seajs 作為一個模塊加載器,所以模塊都是以一個 Module 對象保存在 cachedMods 中的。

var cachedMods = seajs.cache = {}

每個模塊都有 8 種狀態,它一定是在這 8 種狀態之一,而且貌似狀態改變還是不可逆的。

var STATUS = Module.STATUS = {
  // 沒定義狀態值為 0 的狀態常量,這是初始狀態
  // 1 - The `module.uri` is being fetched
  FETCHING: 1,
  // 2 - The meta data has been saved to cachedMods
  SAVED: 2,
  // 3 - The `module.dependencies` are being loaded
  LOADING: 3,
  // 4 - The module are ready to execute
  LOADED: 4,
  // 5 - The module is being executed
  EXECUTING: 5,
  // 6 - The `module.exports` is available
  EXECUTED: 6,
  // 7 - 404
  ERROR: 7
}

其中 SAVED 狀態可以理解為 FETCHED 狀態。除了初始狀態 0 和錯誤狀態 ERROR: 7 之外,其它 6 個狀態都是成對出現的,即 ING 狀態和 ED 狀態,這三對狀態清晰的劃分出來三個處理過程:fetch、load、exec,對應于模塊對象的 3 個方法:fetch()load()exec()

從代碼內容來看,這三個主要過程方法主要功能分別可以用一句話說明:

  • fetch,從 URL 加載模塊定義,得到 factory 函數,并將 factory 函數賦值給對應的模塊對象(通過 Module.get() 創建或獲取);

  • load,fetch 并 load 所有依賴模塊,并在保證所有依賴模塊都是 LOADED 狀態之后,調用入口模塊(_entry)的 callback(貌似只有通過 seajs.use() 創建的匿名模塊才有 callback

  • exec,在調用這個方法的時候,可以保證所有依賴模塊都已經是 LOADED 狀態了,所以 exec就只是簡單的執行 factory 函數,并返回 exports。factory 只執行一次,然后將 exports 緩存下來。

現在來看看入口函數 seajs.use()、模塊定義函數 define()、模塊關系過程處理方法 fetch(),load()exec() 的主要調用關系:

// seajs.use 只調用了 Module.use,所以它們的調用關系可以看作等同
seajs.use = Module.use = function() {
  Module.get()
  exec()        // 通過 _entry.callback 調用
  load()
}
define = Module.define = function() {
  Module.save(id, factory)
}
Module.prototype.fetch = function() {
  define()      // 通過 seajs.request() 調用
  load()
}
Module.prototype.load = function() {
  pass()
  fetch()
  // [遞歸]
  // 結束的條件是 _entry.remain === 1,
  // 即當前是最后一個依賴模塊
  // 遞歸結束時調用 _entry.callback,即調用了 exec
  load()  
}
Module.prototype.exec = function() {
  exec()        // 通過 define 中定義的 factory 函數調用
}
Module.prototype.pass = function() {
  // [遞歸]
  // 結束條件是把 _entry 傳遞到最末一層依賴
  // 遞歸過程通過 _entry.remain 進行了引用記數
  pass()
}

各函數和方法具體處理過程看代碼就明白了,因為遞歸關系有點復雜,還有一些回調關系在里面,所以看起來有點繞,不過還算是看得明白。

http://seajs.org 引用的 seajs 版本是 2.2.1,從這引頁面的控制輸出 seajs.Modules.prototype 來看,并沒有定義 pass() 方法,所以對 _entry 的處理可能會有點不一樣,稍后看了 seajs 2.2.3 版本的代碼就知道了。


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

彭阳县| 金堂县| 闸北区| 浏阳市| 潢川县| 万荣县| 新化县| 旌德县| 织金县| 仪征市| 吉安市| 仁寿县| 彭阳县| 阳城县| 双城市| 阿城市| 东安县| 金川县| 天峨县| 信宜市| 乐陵市| 福安市| 本溪市| 灵石县| 天气| 武山县| 泉州市| 芜湖市| 东宁县| 涿鹿县| 思南县| 临朐县| 南汇区| 陇川县| 友谊县| 宁明县| 房产| 安图县| 河源市| 孟连| 福建省|