【Node.js】node中的一些特色概念,commonJS的模块规范、异步编程和事件循环

前言

现在回头过来看都不知道当时自己写的什么,等以后有空了再来梳理,囧~

commonJS

概念

在commonJS的模块规范没出来之前,js文件的引入只能通过<script>标签进行引入,会有什么问题呢?

  1. 项目变大,意味脚本变多,需要手动管理<script>标签的引入加载顺序来保证项目正常运行。
  2. 不同脚本之间的调用,需要通过全局变量的方式,容易出现全局变量相互覆盖的问题。
  3. node端没有html。

commonJS的模块规范的出现解决了上述的问题,而且后续影响到了浏览器端的js,还有其他技术栈,例如webpack,都加入了commonJS的模块规范。

注意:commonJS规范其实是个大范围的概念,这里只说里面的模块规范。这里放个关系图更好理解:
在这里插入图片描述

例子

function a(n) {
  console.dir("a" + n);
}
function b(n) {
  console.dir("b" + n);
}
function c(n) {
  console.dir("c" + n);
}
module.exports = {
	a,
	b,
	c
}

// 在其他文件内这样引入
var utils = require('./a.js')
utils.a()

exports默认是个导出对象,被导入后是个引用对象,所以在引用文件中修改,被引用文件也能同步更新。

a.js:

exports.a = { 'a': 'a' }

exports.b = function () {
    console.log('b')
}

b.js

var obj = require('./learnNode.js') // 引入
console.log(obj.a) // 使用

当然exports也可以改为其他类型:

a.js:

exports.a = { 'a': 'a' }

exports.b = function () {
    console.log('b')
}

module.exports = function () {
  console.log('我叛变了');
}

b.js

var obj = require('./learnNode.js') // 引入
console.log(obj) 

此时打印出的exports为一个函数,也就是被改变了。但会出现一个怪异现象,如果后获取exports,拿到的并不是改写后的exports,然是之前定义的。可以用定时器实验一下。

a.js:

exports.a = { 'a': 'a' }

exports.b = function () {
    console.log('b')
}

module.exports = function () {
  console.log('我叛变了');
}

setTimeout(() => {
  console.log(exports) // 打出{ a: { a: 'a' }, b: [Function] }
}, 1000)

原理可以通过webpack打包编译后的js文件查看,具体可以看对应极客时间教程09课。

与ES Module的区别

// commonJS
let apiList = require('./a.js')
if (true) {
    apiList = require('./b.js') // 可以动态引入,执行时引入
}

// es module
import apiList from './a.js'
if (true) {
    import apiList from './b.js' // 只能静态引入,编译时报错
}

异步编程

啥是异步这个就不说了,前端老生常谈了。

node里的回调函数是有格式规范的:

  • error-first callback
  • Node-style callback

意思就是回调参数第一个参数是error,第二个是回调执行的结果,node的异步编程其实迭代了几次,目前的用法有Promise、Async(好像不遵循格式规范了?)。

没想到Promise是从node先来的,后来才被加到es6中,所以这里可以直接看【es6入门】Promise与Async的异步写法

事件循环

这个是前端的也不说了。可以直接去做一些题巩固一下:【JS基础】搞定事件循环机制的基础面试题

事件循环比较熟练的这里放个题看看:

try {
  interview(function (err, res) {
    if (err) {
      console.log('cry')
      return;
    }
    console.log(res)
  })
} catch (e) {
  console.log('cry')
}

function interview(callback) {
  setTimeout(() => {
    if (Math.random() > 0.2) {
      callback(null, 'success')
    } else {
      throw new Error('fail');
    }
  }, 500)
}

整个文件用node运行,如果interview抛出问题,为啥是全局node报的错误呢?原因是try catch只能抓到调用堆栈内一个成员,即一个事件循环里的错误,并报给下一个循环去捕捉这个错误。例如A循环里抛出的错误,要到下个B循环里才能捕捉到。

在try函数interview的时候,内部调用了setTimeout,setTimeout是又一个独立循环事件,所以setTimeout的错误interview捕捉不到,所以由全局的node去报这个错误。

改成这样就ok:

try {
  interview(function (err, res) {
    if (err) {
      console.log('cry')
      return;
    }
    console.log(res)
  })
} catch (e) {
  console.log('cry')
}

function interview(callback) {
  if (Math.random() > 0.2) {
    callback(null, 'success')
  } else {
    throw new Error('fail');
  }
}

版权声明:本文为pagnzong原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。