Node.js小白开路(一)-- 全局变量篇

       全局内容是有点相似于咱们在浏览器编程的时候的window对象的,当时在node之中虽然咱们编写的变量会自动的给出上下文环境(模块),可是全局变量的存在仍是大大的帮助了咱们编程时候的便捷性。咱们能够在任意的模块之中使用全局环境下面定义的内容。node

  global其实就至关于在web编写线面的window内容。是为nodeJS中的全局变量内容。其中存储的内容包括,console的全局实例,process信息内容,定时函数内容,Buffer对象内容等,固然还有一些特殊信息的变量。下面咱们一一来讲明。web


  ## 首先咱们来看一看其中连个特殊的全局变量,__dirname和__filename。咱们先来上一段代码吧。npm

-----------------mod.js-----------------
console.log('mod.js的filename:'+ __filename);
console.log('mod.js的dirname:'+ __dirname);

-----------------main.js-----------------
require('./mod.js');

  当咱们运行main.js的时候nodeJS会自动的先将mod.js的内容运行一遍,而后咱们就能够在控制台看到打印出来的信息了。编程

  固然二者之间仍是有些差异的,__dirname 的值其实至关于path.dirname(__filename),__filename展现的是文件的局对路径包括文件名称。而__dirname展现的是文件所在的文件夹的路径信息。json

 


  ## 接下来咱们聊一聊require,module,exports的部分。浏览器

  相信只要稍写过nodeJS的人都知道这3个次。require方法来请求模块,module表示当前文件模块自己,exports内容表示的是模块对外接口部分。缓存

  require内容用于咱们请求相关模块内容,当咱们传入了文件的相对路径给require方法以后,咱们将获得一个引入模块的对象。可是咱们经常使用npm来下载项目支持的模块文件,这是咱们经常只须要输入模块名称以后reqiure会自动的引入相关的内容。这是由于在工程文件之下,在node_modules文件夹之中,咱们用模块名称命名的文件夹,require会一句模块名称到相应的文件夹之中找到默认的index.js文件来进行引入。固然也是能够修更名称的,只是这时咱们须要运用到package.json文件来描述这一模块内容的信息,这样咱们能够指定进入模块的文件。固然require在引入了某一个文件以此以后再次引入的时候实际上是无需再次读取文件的,由于require之中有缓存机制内容,以后再次请求的时候实际上是直接从对应的内存之中获取。在0.3的版本以后require之中添加了require.cache对象以键值对的形式存储,固然咱们能经过键值key的部分来进行缓存内容的删除,那么下一次再加载的时候则模块会从新加载。咱们;爱看一段代码吧:函数

var ser = require('./src/h_server.js');
var buf = require('./src/h_buffer.js');
console.log(require.cache); //引入当前的两个外界模块,打印相关的require.cache内容

结果以下:学习

  这里我截取的是其中一段内容,一个对象属性,能够看到,齐以文件路径内容为变量名称,以module内容座位属性来进行存储,因此咱们要删除档当前对象中的内容的状况只须要经过文件路径查找到module对象并删除就好。ui

  P.S.:包管理器内容(nodeJS文档中与module模块文档下的附加内容)

  这里详细说一下包引入与管理,当咱们在某一文档下建立本身的node工程的时候,咱们经常须要外部引入许多的模块内容,这是咱们经过NPM来下载模块的时候回发现工程目录之下会多出一个文件夹名称为node_module,并为每个下载的外链模块有一个单独的文件夹内容。这时候咱们能够经过模块名称来直接引用相关的模块内容,这是由于包管理器,当咱们内容引入的时候会听从必定的规则内容,这里我引用一张官网的总结内容。

 

从 Y 路径的模块 require(X)
1. 若是 X 是一个核心模块,
   a. 返回核心模块
   b. 结束
2. 若是 X 是以 '/' 开头
   a. 设 Y 为文件系统根目录
3. 若是 X 是以 './' 或 '/' 或 '../' 开头
   a. 加载文件(Y + X)
   b. 加载目录(Y + X)
4. 加载Node模块(X, dirname(Y))
5. 抛出 "未找到"

加载文件(X)
1. 若是 X 是一个文件,加载 X 做为 JavaScript 文本。结束
2. 若是 X.js 是一个文件,加载 X.js 做为 JavaScript 文本。结束
3. 若是 X.json 是一个文件,解析 X.json 成一个 JavaScript 对象。结束
4. 若是 X.node 是一个文件,加载 X.node 做为二进制插件。结束

加载索引(X)
1. 若是 X/index.js 是一个文件,加载 X/index.js 做为 JavaScript 文本。结束
3. 若是 X/index.json  是一个文件,解析 X/index.json 成一个 JavaScript 对象。结束
4. 若是 X/index.node 是一个文件,加载 X/index.node 做为二进制插件。结束

加载目录(X)
1. 若是 X/package.json 是一个文件,
   a. 解析 X/package.json,查找 "main" 字段
   b. let M = X + (json main 字段)
   c. 加载文件(M)
   d. 加载索引(M)
2. 加载索引(X)

加载Node模块(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
   a. 加载文件(DIR/X)
   b. 加载目录(DIR/X)

NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
   a. if PARTS[I] = "node_modules" CONTINUE
   b. DIR = path join(PARTS[0 .. I] + "node_modules")
   c. DIRS = DIRS + DIR
   d. let I = I - 1
5. return DIRS

  固然,在咱们加载模块的时候,当两个相同的模块放在不同的位置的时候加载的时候可能会重复加载并存储两个分加载以后的模块对象,由于齐路径的不相同,因此可能nodeJS没法进行比对。

  当让在咱们引入的模块的时候有的时候会遇到循环引入的状况。这时node极可能会返回一个没有执行完成的模块对象内容来。

 

  ## module内容为模块,在咱们编写每个JS文件的时候,node都认为他是一个模块,而module对象是表明当前编写的这一模块的信息。

  module模块是nodeJS的重要的组成部分,以前戳到nodeJS是以COMMONJS的规则来进行编写的,而module着能够说是组成这一规则的核心内容了。

  因为NODEJS会将每个文件当作一个模块,因此经过node来进入的模块其实是咱们的主体逻辑模块内容,因此咱们又是须要判别当前运行模块是否为主模块,这时咱们能够经过require.main属性来确认。module模块之中会存储文件入口路径,咱们能够经过module中的filename属性来获取,其值与在模块之中调用__filename获取的值多数状况之下是相同的。固然咱们若是想要获取住模块的入口路径咱们也能够经过,require.main.filename来获取,因而可知require中的main属性存储的其实是组模块对象。

  下面说一下模块的包管理器,其原理其实是将模块放入到一个自执行函数之中,这样的好处显而易见了,首先是能够隔绝当前的模块与全局环境,以避免全局环境被污染,同时这样为模块肯定独立的上下文使得每个模块之间的上下文不相互干扰,当某一模块之中与另外一模块之中存在相同名称的命名的时候不会出现冲突的状况,nodeJS的实现方式以下:

(function(exports, require, module, __filename, __dirname) {
// 模块的代码实际上在这里
});

  接下来咱们来看一看module对象之中的某些属性内容:

  -- children:这个属性展现的是一个列表内容,其中存储的是当前模块引用的模块对象。

  -- parent:这个属性则存储的是引用当前模块对象的模块对象。

  -- loaded:这个属性代表的是当前的模块是否已经加载完成了。

  -- path:这个属性存储的是模块的搜索路径。

  -- filename:这个属性内容存储的是模块的彻底解析后的文件名。

  -- id: 模块的标识符。 一般是彻底解析后的文件名。

  -- exports:这个属性是重中之重,属性中存储的是当前模块的公用方法内容,当使用require来引入模块的时候,实际上就是经过这属性来确立的,exports能够理解为模块暴露在大众视线下面的接口。固然咱们也能说得更为的概念化一点,官方文档是如是说的—— exports变量是在模块的文件级别做用域内有效的,它在模块被执行前被赋予 module.exports 的值。

 

  ## exports属性是一个文件级别做用域有效的属性,以前在module之中有提到这一属性,可是module.exports这一属性和单独的exports仍是有些不相同的因此才会将这一属性单独拿出来讲事。exports属性只要是编写的朋友都知道,经过它其实也是能够对module.exports这一属性进行相关的赋值的。例如,当咱们对某一文件下面使用exports对象属性赋予a属性,并对a赋值为‘1’,如展现

exports.a = '1';

这时候nodeJS会自动的将其内容复制给module.exports属性之中。当module.exports指向的对象没有变而只是变化对象内容的时候,则exports指向的对象其实也是会变的。代码以下:

console.log(module.exports); //展现最初的module.exports属性中的内容
console.log('_______________________________'); 

//改变exports的值,看module.exports内容会如何变化。
exports.main = 'main.js';
console.log('exports:');
console.dir(exports);
console.log('module.exports:');
console.dir(module.exports);
console.log('_______________________________');

//改变module.exports的值看exports会如何变化
module.exports.checkData = 'checkData';
console.log('exports:');
console.dir(exports);
console.log('module.exports:');
console.dir(module.exports);
console.log('_______________________________');

//改变module.exports指向的对象,看exports的变化。
module.exports = {
    main:'afterChange'   
}
console.log('exports:');
console.dir(exports);
console.log('module.exports:');
console.dir(module.exports);
console.log('_______________________________');

//再次改变exports的值,看module.exports怎样变化。
exports.checkAgain = 'checkAgain';
console.log('exports:');
console.dir(exports);
console.log('module.exports:');
console.dir(module.exports);
console.log('_______________________________');

相应结果内容:

由上面能够知道,在咱们编写模块的时候初始状况下exports和module.exports是指向同一个对象的,因此exports在改变的时候module.exports也会跟着改变,而当module.exports改变其指向的obj以后咱们能够发现其之间的变化不相关了。因此咱们其实能够这样理解,当初始化的是时候的全局的exports属性和module.exports是指向同一个空的obj的。而当改变了其中一个属性的指向的时候则两个变量将不会再有任何关系。


  ##定时函数内容的学习。

  咱们在编写JS的时候常常会使用到定时函数等内容,来延时执行或者是循环执行某一段逻辑内容固然nodeJS之中也是有相对应的函数的,其中主要的有3个形式的函数内容。分别是setInterval,setTimeout,setImmediate。前两个函数内容就不用多说了,写过JS的都知道,第三个函数的做用是当前事件循环结束的时候立马执行(事件循环以后会有说)。

  下面咱们来了解一下timeout类,这是nodeJS中的一个定时器对象,setTimeout和setInterval对象执行的时候其返回的内容在nodeJS中是一个定时器对象。当定时器对象是活动的状态的时候,则事件循环就会将其添加到的档次循环待执行列表之中。固然nodeJS为咱们添加了两个设置timeout对象活动状态的函数,ref()使得定时器对象处于活动状态,unref()使得当前定时器函数处于非活动状态。

相关文章
相关标签/搜索