模块加载Node.js高级编程:用Javascript构建可伸缩应用(3)2.3 核心API基础

在本篇文章中,我们主要介绍模块加载的容内,自我感觉有个不错的提议和大家分享下

    本系列文章列表和翻译进度,请移步:Node.js高等编程:用Javascript构建可伸缩用应(〇)

    本文对应原文第二分部第三章:Node Core API Basics:Loading Modules

    文章从Word复制到这里,排版特别是缩进与原文不太分歧,可以点这里下载本文的PDF版。

    


    

第三章:加载模块

    

本章容内:

    

  • 加载模块
  • 建创模块
  • 用应node_modules目录

 

    JavaScript是界世上用应率频最高的编程言语之一,它是Web界世的通用言语,被全部浏览器所用应。JavaScript的出生要追溯到Netscape那个代时,它的核心容内被促仓的发开出来,用以抗对Microsoft,与参时当白热化的浏览器大战。由于过早的布发,无可避免的造成了它的一些不太好的特性。

    尽管它的发开时光很短,但是JavaScript仍然备具了很多壮大的特性,不过,每一个脚本享共一个全局命名空间这个特性除外。

    一旦Web页面加载了JavaScript代码,它就会被入注到全局命名空间,会和其他全部已加载的脚本公用同一个地址空间,这会致导很多安全问题,冲突,以及一些常见问题,让bug即难以跟踪又很难决解。

    不过谢天谢地,Node为服务器端JavaScript定了一些标准,还实现了CommonJS的模块准标,在这个准标里,每一个模块有自己的上下文,和其他模块相辨别。这意味着,模块不会污染全局作用域,因为根本就没有所谓的全局作用域,模块之间也不会互相扰干。

    本章,我们将解了几种不同的模块以及如何加载它们。

    把代码拆分红一系列定义精良的模块可以帮你掌控你的用应程序,面下我们将学习如何建创和用应你自己的模块。

    

解了Node如何加载模块

    Node里,可以通过件文径路来引用模块,也可以通过模块名引用,如果用名称引用非核心模块,Node终究会把模块名影射到对应的模块件文径路。而那些含包了核心函数的核心模块,会在Node启动时被先预加载。

    非核心模块括包用应NPM(Node Package Manager)装安的第三方模块,以及你或你的共事建创的当地模块。

    每一个被以后脚本导入的模块会都向程序员露暴一组开公API,用应模块前,需要用require函数来导入它,像这样:

    var module = require(‘module_name’)

    下面的代码会导入一个名为module_name的模块,它是能可个核心模块,也可是以用NPM装安的模块,require函数返回一个含包模块全部大众API的象对。随模块的不同,返回的象对是能可任何JavaScript值,可是以一个函数,也可是以个含包了一系列属性(函数,组数或者任何JavaScript象对)的象对。

    

出导模块

    CommonJS模块系统是Node下件文间享共象对和函数的独一式方。对于一个很杂复的程序,你应当把一些类,象对或者函数重形成一系列精良定义的可重用模块。对于模块用应者说来,模块仅对外露暴出那些你指定的代码。

    在面下的例子里你将会解了到,在Node里件文和模块是一一对应的,我们建创了一个叫circle.js的件文,它仅对外出导了Circle构造函数。

     function Circle(x, y, r) {

                                function r_squared() {

                                         return Math.pow(r, 2);

                                }

                                function area() {

                                         return Math.PI * r_squared();

                                }

                                return {

                                         area: area

                                };

                       }

                       module.exports = Circle;

    代码里最要重的是最后一行,它定义了模块对外出导了什么容内。module是个殊特的变量,它代表以后模块自身,而module.exports是模块对外出导的象对,它可是以任何象对,在这个例子里,我们把Circle的构造函数出导了,这样模块用应者就可以用这个模块来建创Circle实例。

    你也可以出导一些杂复的象对,module.exports被初始化成一个空象对,你把任何你想露暴给外界的容内,作为module.exports象对的属性来出导。比如,你计划了一个模块,它对外露暴了一组函数:

                      function printA() {

                                console.log('A');

                       }

                       function printB() {

                                console.log('B');

                       }

                       function printC() {

                                console.log('C');

                       }

                       module.exports.printA = printA;

                       module.exports.printB = printB;

                       module.exports.pi = Math.PI;

    这个模块出导了两个函数(printA和printB)和一个数字(pi),调用代码看起来像这样:

     var myModule2 = require('./myModule2');

                       myModule2.printA(); // -> A

                       myModule2.printB(); // -> B

                       console.log(myModule2.pi); // -> 3.141592653589793

    

加载模块

    面前提到过,你可以用应require函数来加载模块,不必心担在代码里调用require会影响全局命名空间,因为Node里就没有全局命名空间这个观点。如果模块存在且没有任何语法或初始化误错,require函数就会返回这个模块象对,你还可以这个象对赋值给任何一个局部变量。

    模块有几种不同的类型,大概可以分为核心模块,当地模块和通过NPM装安的第三方模块,根据模块的类型,有几种引用模块的式方,面下我们就来解了下这些知识。

    

加载核心模块

    Node有一些被编译到二进制件文里的模块,被称为核心模块,它们不能通过径路来引用,只能用模块名。核心模块有拥最高的加载优先级,即使已经有了一个同名的第三方模块,核心模块也会被优先加载。

    比如,如果你想加载和用应http核心模块,可以这样做:

             var http = require('http');

    这将返回一个含包了http模块象对,它含包了Node API文档里定义的那些htpp模块的API。

    加载件文模块

    你也可以用应绝对径路从件文系统里加载模块:

    var myModule = require('/home/pedro/my_modules/my_module');

    也可以用一个基于以后件文的相对径路:

                   var myModule = require('../my_modules/my_module');

                       var myModule2 = require('./lib/my_module_2');

    注意下面的代码,你可以略省件文名的扩展名,如果Node找不到这个件文,会实验在件文名后加上js缀后再次找查(译者注:其实除了js,还会找查json和node,详细可以看官网文档),因此,如果在以后目录下存在一个叫my_module.js的件文,会有面下两种加载式方:

                   var myModule = require('./my_module');

                       var myModule = require('./my_module.js');

    加载目录模块

         你还可以用应目录的径路来加载模块:

    var myModule = require('./myModuleDir');

    每日一道理
虽然你现在还只是一株稚嫩的幼苗。然而只要坚韧不拔,终会成为参天大树;虽然你现在只是涓涓细流,然而只要锲而不舍,终会拥抱大海;虽然你现在只是一只雏鹰,然而只要心存高远,跌几个跟头之后,终会占有蓝天。

             Node会假设这个目录是个模块包,并实验在这个目录下搜索包定义件文package.json。

    如果没找到,Node会设假包的口入点是index.js件文(译者注:除了index.js还会找查index.node,.node件文是Node的二进制扩展包,详细见官方文档),以下面代码为例,Node会实验找查./myModuleDir/index.js件文。

    反之,如果找到了package.json件文,Node会实验剖析它,并找查包定义里的main属性,然后把main属性的值看成口入点的相对径路。以本例说来,如果package.json定义如下:

                       {

                                "name" : "myModule",

                                "main" : "./lib/myModule.js"

                       }

    Node就会实验加载./myModuleDir/lib/myModule.js件文

    

从node_modules目录加载

    如果require函数的参数不是相对径路,也不是核心模块名,Node会在以后目录的node_modules子目录下找查,比如面下的代码,Node会实验找查件文./node_modules/myModule.js:

    var myModule = require('myModule.js');

    如果没找到,Node会续继在下级目录的node_modules件文夹下找查,如果还没找到就续继向下层目录找查,直到找到对应的模块或者达到根目录。

    你可以用应这个特性来管理node_modules目录的容内或模块,不过最好还是把模块的管理任务交给NPM(见第一章),当地node_modules目录是NPM装安模块的默许置位,这个计划把Node和NPM关联在了一同。平日,作为发开人员不必太心关这个特性,你可以简略的用应NPM装安,更新和除删包,它会帮你维护node_modules目录

    

存缓模块

    模块在第一次胜利加载后会被存缓起来,就是说,如果模块名被剖析到同一个件文径路,那么每次调用require(‘myModule’)都切确地会返回同一个模块。

    比如,有一个叫my_module.js的模块,含包面下的容内:

    console.log('module my_module initializing...');

    module.exports = function() {

             console.log('Hi!');

    };

    console.log('my_module initialized.');

    然后用面下的代码加载这个模块:

    var myModuleInstance1 = require('./my_module');

    它会发生面下的输出:

    module my_module initializing...

    my_module initialized

    如果我们两次导入它:

    var myModuleInstance1 = require('./my_module');

    var myModuleInstance2 = require('./my_module');

    输出仍然是:

    module my_module initializing...

    my_module initialized

    也就是说,模块的初始化代码仅执行了一次。当你构建自己的模块时,如果模块的初始化代码里含有可能发生反作用的代码,一定要特别注意这个特性。

    

小结

    Node取消了JavaScript的默许全局作用域,转而用采CommonJS模块系统,这样你可以更好的组织你的代码,也因此避免了很多安全问题和bug。可以用应require函数来加载核心模块,第三方模块,或从件文及目录加载你自己的模块

    还可以用相对径路或者绝对径路来加载非核心模块,如果把模块放到了node_modules目录下或者对于用NPM装安的模块,你还可以直接用应模块名来加载。

 

    译者注:

    提议读者把官方文档的模块章节读阅一遍,个人感觉比作者讲得更晰清了明,而且还附加了一个非常有具代表性的例子,对解理Node模块加载会很有很大助帮。面下把那个例子也引用过去:

    用require(X) 加载径路Y下的模块

    1. 如果X是核心模块,

       a. 加载并返回核心模块

       b. 结束

    2. 如果X以 './' or '/' or '../ 开始'

       a. LOAD_AS_FILE(Y + X)

       b. LOAD_AS_DIRECTORY(Y + X)

    3. LOAD_NODE_MODULES(X, dirname(Y))

    4. 抛出异常:"not found"

     

    LOAD_AS_FILE(X)

    1. 如果X是个件文,把 X作为JavaScript 脚本加载,加载毕完后结束

    2. 如果X.js是个件文,把X.js 作为JavaScript 脚本加载,加载毕完后结束

    3. 如果X.node是个件文,把X.node 作为Node二进制插件加载,加载毕完后结束

     

    LOAD_AS_DIRECTORY(X)

    1. 如果 X/package.json件文存在,

       a. 剖析X/package.json, 并找查 "main"字段.

       b. 另M = X + (main字段的值)

       c. LOAD_AS_FILE(M)

    2. 如果X/index.js件文存在,把 X/index.js作为JavaScript 脚本加载,加载毕完后结束

    3. 如果X/index.node件文存在,把load X/index.node作为Node二进制插件加载,加载毕完后结束

     

    LOAD_NODE_MODULES(X, START)

    1. 另DIRS=NODE_MODULES_PATHS(START)

    2. 对DIRS下的每一个目录DIR做如下操纵:

       a. LOAD_AS_FILE(DIR/X)

       b. LOAD_AS_DIRECTORY(DIR/X)

     

    NODE_MODULES_PATHS(START)

    1. 另PARTS = path split(START)

    2. 另ROOT = index of first instance of "node_modules" in PARTS, or 0

    3. 另I = count of PARTS - 1

    4. 另DIRS = []

    5. while I > ROOT,

       a. 如果 PARTS[I] = "node_modules" 则续继后续操纵,否则下次循环

       c. DIR = path join(PARTS[0 .. I] + "node_modules")

       b. DIRS = DIRS + DIR

       c. 另I = I - 1

    6. 返回DIRS

文章结束给大家分享下程序员的一些笑话语录: 神灯新篇
一个程序员在海滩上发现了一盏神灯。他在灯上擦了几下,一个妖怪就从灯里跳出来说:“我是世界上法术最强的妖怪。我可以实现你的任何梦想,但现在,我只能满足你一个愿望。”程序员摊开了一幅中东地图说:“我想让中东得到永久的和平。”妖怪答道:“哦,我没办法。自打创世纪以来,那里的战火就没有停息过。这世上几乎没有我办不到的事,但这件事除外。”程序员于是说:“好吧,我是一个程序员,为许多用户编写过程序。你能让他们把需求表述得更清楚些,并且让我们的软件项目有那么一两次按进度按成本完成吗?”妖怪说:“唔,我们还是来看中东地图吧。”


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值