惱人的命名衝突node
煩瑣的文件依賴程序員
經過 exports 暴露接口。這意味着不須要命名空間了,更不須要全局變量。這是一種完全的命名衝突解決方案。數組
經過 require 引入依賴。這可讓依賴內置,開發者只需關心當前模塊的依賴,其餘事情 Sea.js 都會自動處理好。對模塊開發者來講,這是一種很好的 關注度分離,能讓程序員更多地享受編碼的樂趣。緩存
1. seajs.config 2. seajs.use 3. define 4. require 5. require.async 6. exports 7. module.exports
用script
標籤引入sea.js文件,seajs.config(data)
啓動配置函數,config
函數會會合並全部config
配置,seajs.use = function(ids, callback)
,啓用主腳本異步
主腳本啓動以後,首先利用request
模塊請求主腳本(生成script標籤插入head標籤中),而後根據正則解析模塊define
的依賴,並對依賴遞歸解析其依賴。
在運行過程當中,經過監聽發佈者模式,系統內置了8個事件,可用於開發插件。async
resolve -- 將 id 解析成爲 uri 時觸發 load -- 開始加載文件時觸發 fetch -- 具體獲取某個 uri 時觸發 request -- 發送請求時觸發 define -- 執行 define 方法時觸發 exec -- 執行 module.factory 時觸發 config -- 調用 seajs.config 時觸發 error -- 加載腳本文件出現 404 或其餘錯誤時觸發
全部相關數據最後所有掛載在window.seajs
下,包括方法及模塊數據。函數
exports 僅僅是 module.exports 的一個引用。在 factory 內部給 exports 從新賦值時,並不會改變 module.exports 的值。所以給 exports 賦值是無效的,不能用來更改模塊接口。fetch
//源碼以下 // Exec factory var factory = mod.factory; var exports = isFunction(factory) ? factory.call(mod.exports = {}, require, mod.exports, mod) : factory
有時會但願可使用 require 來進行條件加載:ui
if (todayIsWeekend) require("play"); else require("work");
但請牢記,從靜態分析的角度來看,這個模塊同時依賴 play 和 work 兩個模塊,加載器會把這兩個模塊文件都下載下來。 這種狀況下,推薦使用 require.async 來進行條件加載。編碼
//sea.js源碼以下 require.async = function(ids, callback) { //可傳入回調函數 Module.use(ids, callback, uri + "_async_" + cid()) //——async_英語標識這個腳本是異步加載的,cid用於清除緩存 return require //返回require方便鏈式調用 }
a.js
define(function(require, exports) { var b = require('./b'); var c = require('./c'); });
Sea.js 在運行 define 時,接受 factory 參數,能夠經過 factory.toString() 拿到源碼,再經過正則匹配 require 的方式來獲得依賴信息。依賴信息是一個數組,好比上面 a.js 的依賴數組是:['./b', './c']
//源碼以下 // Parse dependencies according to the module factory code if (!isArray(deps) && isFunction(factory)) { deps = typeof parseDependencies === "undefined" ? [] : parseDependencies(factory.toString()) //parseDependencies是利用正則解析依賴的一個函數 }
// Emit event, firing all bound callbacks. Callbacks receive the same // arguments as `emit` does, apart from the event name var emit = seajs.emit = function(name, data) { var list = events[name] if (list) { // Copy callback lists to prevent modification list = list.slice() // Execute event callbacks, use index because it's the faster. for(var i = 0, len = list.length; i < len; i++) { list[i](data) } } return seajs }
主要看這個部分list = list.slice()
,註釋是防止拷貝該時間的回調函數,防止修改,困惑了一下。
緣由是Javascript中賦值時,對於引用數據類型,都是傳地址。
因此這裏,若是想防止觸發事件的過程當中回調函數被更改,必須對這個list數組進行拷貝,而並不是只是將list指向events[name]的地址。
// Remove the script to reduce memory leak if (!data.debug) { head.removeChild(node) }
這裏思考了蠻久,爲何能夠刪除動態插入的腳本?這樣腳本還會生效嗎?
首先,必須瞭解計算機內存分爲
靜態數據區 (用來存放程序中初始化的全局變量的一塊內存區域)
代碼區 (一般用來存放執行代碼的一塊內存區域)
棧區 (棧在進程運行時產生,一個進程有一個進程棧。棧用來存儲程序臨時存放的局部變量,即函數內定義的變量 不包括static 類型的。函數被調用時,他的形參也會被壓棧。
堆區 (用於存放進程運行中被動態分配的內存段,它的大小而且不固定,可動態擴展。當進程調用malloc等分配內存時,新分配的內存被動態的添加到堆上(堆被擴大),當利用free等函數釋放內存時,被釋放的‘ 內存從堆中剔除)
這些在Javascript中都被屏蔽了,大部分時候咱們都不須要考慮,可是若是要深刻了解的話,則是必需要知道的知識。
首先HTML文檔中的JS腳本在計算機中做爲指令被讀入內存,以後開始執行,CPU開始一條一條指令讀取,好比,讀取到var cool = "wilson"
時,就會在內存中分配一個6字符大小的內存,一個function
也同樣會在內存中佔據必定大小。因此,當指令所有運行完以後,指令自己其實已經沒有用了,可是仍然給佔據了一部份內存。
當你點擊按鈕觸發一個回調函數時,並不是去讀取指令,而是讀取內存中這個回調函數的地址。因此刪除這些動態加載的JS文件是沒有問題的。
所謂 ID 和路徑匹配原則 是指,使用 seajs.use 或 require 進行引用的文件,若是是具名模塊(即定義了 ID 的模塊),會把 ID 和 seajs.use 的路徑名進行匹配,若是一致,則正確執行模塊返回結果。反之,則返回 null。
// x.js define(function(require, exports, module) { // 錯誤用法 setTimeout(function() { module.exports = { a: "hello" }; }, 0); }); //在 y.js 裏有調用到上面的 x.js: // y.js define(function(require, exports, module) { var x = require('./x'); // 沒法馬上獲得模塊 x 的屬性 a console.log(x.a); // undefined });
WilsonLiu's blog首發地址:http://blog.wilsonliu.cn