Nodejs學習第9天

在Node.js中,有一個簡單的模塊加載系統,在Node.js的模塊中,可以直接和文件通信,不僅僅是*.js文件。比如之前的栗子(同目錄下):

 var server = require('./server'),
     router = require('./router'),
     dispatcher = require('./dispatcher');

 var handle = {} ;
 handle['/'] = dispatcher.root ;
 handle['/start'] = dispatcher.start ;
 handle['/upload'] = dispatcher.upload ;
 server.start(handle,router.route) ;

server.js中有導出一個函數#start(object[,....)

var http = require('http'),
    url = require('url') ;

 function start(hander,route) {
  function onRequest(req,res) {
   var pathName = url.parse(req.url).pathname ;
   console.log('request path ',pathName) ;

   var postData = '' ;
   req.setEncoding("utf8");
   req.addListener('data',function(postDataChunk){
    //
    postData = postDataChunk ;
   }) ;

   //end
   req.addListener('end',function(){
    //
    route(pathName,hander,res,postData) ;
   }) ;

   //dispatcher
   //route(pathName,hander,res) ;
  }

  http.createServer(onRequest).listen(8081) ;
  console.log('server has started.') ;
 }

 exports.start = start ;

這里直接使用了 exports這個特殊對象導出函數#start , 將#start 這個函數新增到exports對象上,然后使用require('./server') 方式,Node.js模塊系統會幫我們獲取到exports這個對象,進而可以使用之前新增(導出)到該對象的屬性/函數.

值得注意的是,自定義模塊導出的寫法需要進行區別,方式一:

module.exports = {
    fun: function(name) {
        console.log('hello, ',name) ;
    }
} ;

call:

var nd = require('./nd') ;
nd.fun('palm') ;
//Print
hello,  palm

這種導出改變了module.exports對象的指向,之前module.exports 為一個空對象, 如:

module.exports = {} ;

方式二:

/*module.exports = {
    fun: function(name) {
        console.log('hello, ',name) ;
    }
} ;*/

exports.fun = function(name){
    console.log('hello, ',name) ;
} ;

這里就是在空對象 - exports上新增一個函數,類似平時編寫js時,將相同業務的功能函數歸類編寫,方便使用,如:

var util = util || {} ;
util.print = function(msg) {
    if (console.log) {
        console.log(' pring -- ',msg) ;
    }
} ;

不太一樣的是,上面需要使用var 聲明變量,而exports不用我們聲明,因為這是Node.js內置對象.

方式三:

/*module.exports = {
    fun: function(name) {
        console.log('hello, ',name) ;
    }
} ;*/

function fn(name){
    console.log('hello, ',name) ;
} ;

exports.fun = fn ;

這種方式其實和方式二一毛一樣,只是個人愛好而已,有些人喜歡事情一件一件的做,什么都準備好了,再統一導出,就像某些廚師,菜洗好、姜、蔥、蒜等都弄好了,放那,才開始開火炒菜; 有些人呢,喜歡編寫完一個導出一個,可能油都冒煙兒了才準備摘菜 ~ 可能有點晚了哈 ~ -_-#。這兩種,在我看來,全憑個人好惡, 沒有實質區別,不過我們在使用方式二/三的時候需要注意這種情況:

/*module.exports = {
    fun: function(name) {
        console.log('hello, ',name) ;
    }
} ;*/

/*function fn(name){
    console.log('hello, ',name) ;
} ;
exports.fun = fn ;*/

exports = {
    fun : function(name) {
        console.log('hello, ',name) ;
    }
} ;

這時候使用node 命令運行,shell立馬告訴我們出錯了:

[palm@arch]: ~/Desktop/node-stu/stunode>$ node nd-test.js 
/home/palm/Desktop/node-stu/stunode/nd-test.js:2
nd.fun('palm') ;
   ^

TypeError: nd.fun is not a function
    at Object.<anonymous> (/home/palm/Desktop/node-stu/stunode/nd-test.js:2:4)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.runMain (module.js:604:10)
    at run (bootstrap_node.js:394:7)
    at startup (bootstrap_node.js:149:9)
    at bootstrap_node.js:509:3

告訴我們有TypeError(javascript Error子類),nd.fun is not a function 沒有找到函數fun 或者fun 不是一個函數 ,為什么呢?
把require到的對象打印出來:

var nd = require('./nd') ;
// nd.fun('palm') ;

console.log(nd) ;  //print : {}

是一個空對象,在nd.js文件中,把exports對象打印出來:


console.log(' before ',exports) ;
exports = {
    fun : function(name) {
        console.log('hello, ',name) ;
    }
} ;

console.log(' after ',exports) ;

//print
 before  {}
 after  { fun: [Function: fun] }

嗯? 函數fun 在exports中啊,為毛到另一個文件中exports對象就變成空對象了?原來exports雖然也是一個全局變量,但它只是模塊內為了方便通過exports訪問module.exports 的一個module-global, exports是指向module.exports的一個引用,類似一個指針,如果我們將一個新的對象重新指向exports 則,exportsmodule.exports就不再綁定到一起了,我想在Node.js module system創建 module.exports對象的時候代碼代碼大概如下:

exports = module.exports = {} ;

module.exports 是Node.js模塊系統創建的,導入模塊傳遞的也是該變量,而不是exports。
require 導入模塊后從module.exports 取出內容后值為空,解釋在這里 require函數類似如下片段:

function require(...) {
  // ...
  ((module, exports) => {
    // Your module code here
    exports = some_func;        // re-assigns exports, exports is no longer
                                // a shortcut, and nothing is exported.
    module.exports = some_func; // makes your module export 0
  })(module, module.exports);
  return module;
}

所以,在編寫自定義模塊的時候極力建議使用第一種方式,或者:

function fun0(name) {
    console.log('hello, ',name) ;
}

function fun1(arg) {
    console.log('arg') ;
}

module.exports.fn = fun ;
module.exports.fn1 = fun1 ;

模塊內變量
在Node.js中,模塊內var a = 'xxx' 聲明變量,都是私有的,如果沒有使用exports導出外部均不能使用,Node.js使用了一個官方稱為module wrapper 的東西進行處理,通俗的說就是將我們編寫的代碼使用function 包了一下,為什么使用function包一層就可能達到模塊本地變量私有化的目的呢? 這是因為在javascript中,沒有像java 或者python 類似的塊級作用域,在javascript中,只有函數級作用域,函數內部生命變量外部不可以訪問,如:

function fn() {
    var fnx1 = 1234 ;
}

console.log(' fn.fnx1 ', fnx1) ;

node.js shell 提示如下:

console.log(' fn.fnx1 ', fnx1) ;
                         ^

ReferenceError: fnx1 is not defined
    at Object.<anonymous> (/home/palm/Desktop/node-stu/stunode/nd.js:44:26)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.runMain (module.js:604:10)
    at run (bootstrap_node.js:394:7)
    at startup (bootstrap_node.js:149:9)
    at bootstrap_node.js:509:3

chrome中,console錯誤提示:

js.js:46 Uncaught ReferenceError: fnx1 is not defined

在模塊代碼執行之前,Node.js會將自定義模塊代碼包裝成如下這樣:

(function (exports, require, module, __filename, __dirname) {
// Your module code actually lives in here
});

//__filename和__dirname 是模塊內文件屬性的內置變量 
//__filename包含了模塊文件的絕對路徑 ; 
//__dirname 包含模塊文件的目錄path.

如此,模塊內本地變量都是私有的,在工作中,也時常見到猿類利用js這個特性來將某些變量隱藏,或者給老板節省一點兒內存開銷, 或者相同變量沖突. 如:

var arr = [] ;

for(var i = 0; i < 50; i++ ){
    var node = {}, filename = '/home/palm/Desktop/node-stu/stunode/nd.js' ;
    node['__filename'] = filename;
    node['xx'] = 'test' ;

    arr.push(node) ;
}


for(var i = 0; i < 50; i++ ){
    var node = {}, filename = '/home/palm/Desktop/node-stu/stunode/nd-test.js' ;
    node['__filename'] = filename;
    node['xx'] = 'test' ;

    arr.push(node) ;
}

以上代碼,四個變量nodex2 、 filenamex2 其實都是全局變量, 如:

var arr = [] ;

for(var i = 0; i < 50; i++ ){
    var node = {}, filename = '/home/palm/Desktop/node-stu/stunode/nd.js' ;
    node['__filename'] = filename;
    node['xx'] = 'test' ;

    arr.push(node) ;
}


for(var i = 0; i < 50; i++ ){
    var node = {}, filename = '/home/palm/Desktop/node-stu/stunode/nd-test.js' ;
    node['__filename'] = filename;
    node['xx'] = 'test' ;

    arr.push(node) ;
}

console.log(node,filename) ;

//print:

{ 
__filename: '/home/palm/Desktop/node-stu/stunode/nd-test.js',
  xx: 'test' 
} 
'/home/palm/Desktop/node-stu/stunode/nd-test.js'

為了避免自己的的私有變量被污染或者變量常駐內存而導致內存泄漏,可以采用如下方式改變這一狀況,如:

var arr = [] ;

(function() {
    for(var i = 0; i < 50; i++ ){
        var node = {}, filename = '/home/palm/Desktop/node-stu/stunode/nd.js' ;
        node['__filename'] = filename;
        node['xx'] = 'test' ;

        arr.push(node) ;
    }
})() ;


(function() {
    for(var i = 0; i < 50; i++ ){
        var node = {}, filename = '/home/palm/Desktop/node-stu/stunode/nd-test.js' ;
        node['__filename'] = filename;
        node['xx'] = 'test' ;

        arr.push(node) ;
    }
})() ;

console.log(node,filename) ;

執行上面代碼,一定會輸出 ReferenceError ,因為確實找不到 這兩個變量定義了。如:

console.log(node,filename) ;
            ^

ReferenceError: node is not defined
    at Object.<anonymous> (/home/palm/Desktop/node-stu/stunode/nd.js:63:13)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.runMain (module.js:604:10)
    at run (bootstrap_node.js:394:7)
    at startup (bootstrap_node.js:149:9)
    at bootstrap_node.js:509:3

這里能arr 變量能訪問是因為arr全局變量,js中,作用域內函數可以訪問作用域內任何變量(作用域內其他函數內部變量除外)。

這里有更多關于module對象屬性的功能描述和用法。

js模塊化編程已經是一個趨勢了,各種js框架都在積極推進這一工作,例如:

  • RequireJS 在推廣過程中對模塊化的規范定義 -- AMD
  • SeaJS 在推廣過程中對模塊化的規范定義 -- CMD
  • BravoJS 的 CommonJS Modules/2.0 規范等

目的是為了解決前端js代碼混亂難以維護、代碼復用率低等問題.尤其是在瀏覽器端。

module的學習到這里就結束了,如果看到有錯誤的地方,請幫我指出來,我將感激不盡!

!~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 模塊 Node 有簡單的模塊加載系統。在 Node 里,文件和模塊是一一對應的。下面例子里,foo.js加載同一個...
    保川閱讀 610評論 0 0
  • topics: 1.The Node.js philosophy 2.The reactor pattern 3....
    宮若石閱讀 1,126評論 0 1
  • Node.js是目前非常火熱的技術,但是它的誕生經歷卻很奇特。 眾所周知,在Netscape設計出JavaScri...
    w_zhuan閱讀 3,639評論 2 41
  • Node.js是目前非常火熱的技術,但是它的誕生經歷卻很奇特。 眾所周知,在Netscape設計出JavaScri...
    Myselfyan閱讀 4,101評論 2 58
  • (秋月未出,亦出行,無與為樂,舊景觸及回憶,即作此篇) 清月幺幺,遍數星辰;涼風習習,盡然草深。湖河水光不泛,路橋...
    笑一弦雨閱讀 468評論 0 1