NodeJs入门第一课

NodeJs慕课课程笔记

Posted by EWL on October 7, 2018

nodejs对于前端开发的好处

前端职责扩大,统一开发体验 适合于高并发 I/O密集的场景(web就是典型的I/O密集场景,例如读取静态资源、网络请求I/O等等)

nodejs简要相关

nodejs自己的模块规范是CommonJS。 global对象是nodejs的全局对象,类似于浏览器上的window,上面挂载着常用的全局属性和方法

本次的学习内容是从环境以及调试入手,分别有以下内容:

  • commonJS
  • 引用系统内置模块/引用第三方模块
  • global变量
  • process进程
  1. commonJS 不同于浏览器的requireJS这样的模块管理系统,nodejs拥有自带的模块管理系统,即commonJS。
  • 每一个文件就是一个模块,每一个模块都有自己的作用域(防止全局变量污染)
  • 在模块的内部module就代表着模块本身
  • 每一个模块的module.exports表示该模块的出口,可以向其他模块输出一些变量属性或者是方法函数。
  • 每一个模块在加载时就会执行,而一旦加载过下一次再进行加载时就会从缓存中抽取内容了,比如代码中如果有console.log这种操作,那么再次进行require的话就只会打印第一次。这一点可以用于解释Nodejs中循环调用的结果。当然,要避免循环引用。

    疑问01:关于循环引用的解释

  • 在使用require进行模块引用的时候,会在js json node这三种文件后缀名中依次进行对比检查,检查到之后就进行引用,都检查不到的话就报错。常用的自然是js 和 Json, node就比较少用。 这也就说明require文件的时候可以不用书写文件后缀名。
  • 在模块中书写的变量会变成模块的私有变量,而书写在global上的变量会变成整个的全局变量,挂载在global这个全局变量上,如果需要向全局暴露一些内容,可以通过给global变量添加属性的方式进行。

在chrome中可以使用chrome://inspect来调试对应的node应用,从中可以看到单个node模块的全貌, 如下。 image.png 点击target部分的inspect就可以进入调试页面 image.png

可以看到单个模块的代码全部被包裹在一个function中,并且该function有五个参数,分别对应

  • exports(一个对象)
  • require (一个方法函数,用于调用其他模块)
  • module (一个对象,里面有众多属性,包括常见的exports,前面做过介绍,表示当前模块)

疑问02: exports和module.exports有何区别?

  • __filename (当前文件的文件路径)
  • __dirname (当前文件所在文件夹的路径)
  1. global全局对象 类似于浏览器环境下的全局变量window,nodejs也有自己的全局变量global,当然这两个全局变量下面拥有的属性方法自然不尽相同,但是即使在nodejs下面错误地使用了浏览器环境下的变量或者方法也不会出现报错的情况,只是会提示undefined。 其次,nodejs中并没有浏览器中的DOM或者是BOM,它只包含了核心的ECMAScript。

  2. process 表示当前执行的进程,它挂载在global下,它同样是一个对象,拥有一系列属性。本次学习的有主要四个常用的属性。

    • argv (参数 , 包括了程序所在的路径,以及当前执行文件的文件路径,执行脚本命令的后续参数,比如(node test.js –test testVal01=1 testVal02=2,此时的argv就是 Node的路径,test.js的文件路径,以及–test,testVal01=1,testVal02=2))
    • argv0 (argv的首个参数值的引用 )
    • execArgv (字面意思可以翻译为执行参数,即node –inspect test.js –test testVal01=1 testVal02=2里面的–inspect,且该变量是一个数组)
    • execPath (字面意思为执行路径,表示执行这个脚本是从哪里执行的,即node所在的路径 )

解决疑惑

  • 疑问01: 举例来说,假设有两个模块A与B

A模块如下:

module.exports.test = 'A';

const modB = require('./04_modB');

console.log('modB.test in modA',modB.test);

module.exports.test = 'AA';

B模块如下:

module.exports.test = 'B';

const modA = require('./03_modA');

console.log('modA.test in modB', modA.test);

module.exports.test = 'BB';

同时引用了这两个模块的文件如下:

const modA = require('./03_modA');
const modB = require('./04_modB');

console.log(modA.test);
console.log(modB.test);

image.png

为什么会出现第一句的情况,这就要从nodejs中模块加载执行的规则说起,一旦模块加载了就不会再进行一次加载,只会从上一次加载的内容中抽取内容进行剩余代码的执行,那么此时就会出现,在main文件中,当A模块在加载时,逐行执行到加载B模块的部分,就直接进入到B模块中逐行执行B模块的内容,恰好在B模块中遇到了加载A模块的代码,此时A模块已经加载过了,那么就会从已加载的部分中抽取需要的内容,此时的module.exports.test仍然还是,并没有执行到修改test为AA的部分,所以输出为A,这就是为什么第一行输出的是A而不是AA。等到B模块全部加载结束,就又回到A模块的部分,继续向下执行,然后全部执行结束之后,再继续进行main中B模块的加载流程,同样的流程加载结束之后,再去打印二者的test时就已经是修改过的AA与BB了。

  • 疑问02: exports 实际上是module.exports的一个快捷方式,可以修改它的属性,但是不可以直接修改它的指向,像是通过对象字面量覆盖的方式,会使得exports不再是module.exports的快捷方式了,而是直接指向了另一个对象,在这样的情况下,别的模块来引用该模块的时候,输出就会变成undefined。