前言:因为以前学习Node.js并没有真正学过,只是粗略的学习了npm的常用命令和Node.js的一些模块化语法,所以昨天花了很多时间一天看完一本书《Node.js开发指南》。通过这本书,让我对Node.js有了更全面的了解,但是因为这本书出版时间太早,一些API已经改变或者被废弃了,而对于学习Node.js来说,最核心的部分是最重要的部分,于是我配合官方文档总结整理了本书的第四章——Node.js核心。由于水平有限,如有遗漏和错误,敬请指正。文本核心模块是Node.js的核心。它主要由一些精简高效的库组成(这方面和Python很相似),为Node.js提供了基础的API。主要内容包括:Node.js核心介绍(一)全局对象常用工具和事件机制Node.js核心介绍(二)文件系统访问HTTP服务器和客户端全局对象全局对象我想学过JavaScript的人都知道浏览器是window,在程序的任何地方都可以访问全局对象,但是在Node.js中,这个全局对象被global代替了,所有的全局变量(除了global本身)都是全局对象的属性。我们在Node.js中可以直接访问的对象通常都是全局属性,比如:console、process等。全局对象和全局变量最根本的作用就是作为全局变量的宿主。根据ECMAScript规范,满足以下条件的变量为全局变量:定义在最外层的变量(在Node.js中不存在,因为Node.js的代码是在一个模块中执行的,有是最外层没有定义的变量)全局对象的属性隐式定义的变量(即没有定义而是直接赋值的变量)当我们定义一个全局变量时,全局变量会自动成为的属性全局变量。process进程对象是一个全局变量,提供当前Node.js进程的信息,控制当前Node.js进程。通常我们在编写本地命令行程序的时候,都要处理一下。以下是它最常用的成员方法:1.process.argvprocess.argv属性返回一个数组,里面包含了启动Node.js进程时的命令行参数。第一个元素是process.execPath,第二个元素是当前执行的JavaScript文件的路径,其余元素是其他命令行参数。例如存储一个名为argv.js的文件://printprocess.argvprocess.argv.forEach((val,index)=>{console.log(`${index}:${val}`);});然后命令行如下所示:$nodeprocess-args.jsonetwo=threefour0:/usr/local/bin/node1:/Users/mjr/work/node/process-args.js2:one3:two=three4:42.process.stdoutprocess.stdout是标准输出流,通常我们使用console.log()输出打印字符,process.stdout.write()函数提供了更底层的接口。process.stdout.write('请输入num1的值:');3.process.stdinprocess.stdin是标准输入流,最初是挂起的,要从标准输入中读取数据,必须恢复流,并手动为流编写事件响应函数。/*1:声明变量*/varnum1,num2;/*2:输出到屏幕,提示信息,需要输入num1*/process.stdout.write('请输入num1的值:');/*3:监听用户的输入*/process.stdin.on('data',function(chunk){if(!num1){num1=Number(chunk);/*4:输出到屏幕,提示信息,请求进入num2*/process.stdout.write('请输入num2的值');}else{num2=Number(chunk);process.stdout.write('结果为:'+(num1+num2));}});4.process.nextTick(callback[,...args])...args调用时传递给回调的附加参数。process.nextTick()方法将回调添加到“下一个报价队列”。当前事件轮询队列中的任务全部完成后,将依次调用下一个tick队列中的所有回调。此方法不是setTimeout(fn,0)的别名。它更高效,所以不要使用setTimeout代替process.nextTick。事件循环对滴答的后续调用在任何I/O事件(包括计时器)之前运行。console.log('start');process.nextTick(()=>{console.log('nextTickcallback');});console.log('scheduled');//开始//scheduled//nextTick回调4.consoleconsole模块提供了一个简单的调试控制台,类似于web浏览器提供的JavaScript控制台。该模块导出两个特定组件:一个Console类,具有方法console.log()、console.error()和console.warn(),可用于写入任何Node.js流。可用于写入process.stdout和process.stderr的全局控制台实例。使用全局控制台时无需调用require('console')。(注意:全局控制台对象的方法既不总是同步的(如浏览器中的类似API),也不总是异步的(如在其他Node.js流中)。例如,全局上下文中的常见控制台实例:console.log('hello,world');//hello,worldconsole.log('hello%s','world');//helloworldconsole.error(newError('错误信息'));//错误:错误信息constname='description';console.warn(`warning${name}`);//警告描述console.trace()//将当前调用输出到标准错误流StackcommonConsoleclass:constout=getStreamSomehow();consterr=getStreamSomehow();constmyConsole=newconsole.Console(out,err);myConsole.log('hello,world');//你好,worldmyConsole.log('hello%s','world');//helloworldmyConsole.error(newError('errormessage'));//错误:错误信息constname='description';myConsole.warn(`warning${name}`);//警告说明常用工具utilutil模块主要用于支持Node.js内部API的需求。大多数实用程序也可供应用程序和模块开发人员使用,以补充核心JavaScript的功能。可以这样调用:constutil=require('util');1.util.inspect(object[,options])util.inspect()方法返回object的字符串表示,主要用于调试和错误输出。附加选项可用于更改格式字符串的某些方面。至少接受一个参数objet,为要转换的参数,option可选,可选内容如下:showHidden如果为true,object的不可枚举符号和属性也会被包含转换后结果中的格式。默认为假。depth指定格式化对象时递归的次数。这对于查看大型复杂对象很有用。默认为2,传入null无限递归。colors如果为true,则输出样式使用ANSI颜色代码。默认为false,可以自定义。customInspect如果为false,则不会调用对象上的自定义inspect(depth,opts)函数。默认为真。showProxy如果为真,Proxy对象和函数将显示它们的目标和处理程序对象。默认为假。maxArrayLength指定格式化时可以包含的数组和TypedArray元素的最大数量。默认值为100。设置为null以显示所有数组元素。设置为0或负数以隐藏数组元素。breakLength要分行的对象键的长度。设置为Infinity将对象格式化为单行。默认为60。例如,查看util对象的所有属性:constutil=require('util');console.log(util.inspect(util,{showHidden:true,depth:null}));2.util.callbackify(original)util.callbackify(original)方法将异步函数(或返回Promise的函数)转换为Node.js回调函数。回调函数中,第一个参数err为Promise被拒绝的原因(如果Promise状态为resolved,err为null),第二个参数为Promise状态为resolved时的返回值。例如:constutil=require('util');异步函数fn(){returnawaitPromise.resolve('helloworld');}constcallbackFunction=util.callbackify(fn);callbackFunction((err,ret)=>{if(err)throwerr;console.log(ret);});//helloworld注:回调函数是异步执行的,有异常栈错误跟踪。如果回调函数抛出异常,进程将触发'uncaughtException'异常,如果没有被捕获,进程将退出。Null作为回调函数中的参数具有特殊含义。如果回调函数的第一个参数是Promise被拒绝的原因,并且有返回值,并且该值可以转化为布尔值false,这个值会被封装在Error对象中,可以通过属性reason获取。functionfn(){returnPromise.reject(null);}constcallbackFunction=util.callbackify(fn);callbackFunction((err,ret)=>{//当Promise的rejecte为null时,其Error和原始值将存储在'reason'中。err&&err.hasOwnProperty('reason')&&err.reason===null;//true});事件驱动事件事件是Node.js最重要的模块,因为Node.js本身的架构是基于事件的。大多数Node.js核心API都使用通常的异步事件驱动架构,它提供了独特的接口,因此可以称为Node.js事件的及时编程。events模块不仅用于用户代码与底层Node.js事件循环交互,而且几乎所有模块都依赖它。所有可以发出事件的对象都是EventEmitter类的实例。这些对象公开了一个eventEmitter.on()函数,该函数允许将一个或多个函数绑定到将由对象触发的命名事件。事件名称通常是驼峰式字符串,但可以使用任何有效的JavaScript属性名称。对于每个事件,EventEmitter支持多个事件侦听器。当一个事件发出时,依次调用注册到该事件的事件监听器,并将事件参数作为回调函数参数传递。例如:constEventEmitter=require('events');classMyEmitterextendsEventEmitter{}constmyEmitter=newMyEmitter();//eventEmitter.on()方法用于注册监听器myEmitter.on('event',()=>{console.log('一个事件被触发!');});//eventEmitter.emit()方法用于触发事件myEmitter.emit('event');让我们看一下EventEmitter最常用的API:-EventEmitter.on(event,listener)方法在名为eventName的事件的侦听器数组的末尾添加一个侦听器函数。不检查是否已添加侦听器。多次调用相同的eventName和listener会导致多次添加和调用listener。-emitter.prependListener(eventName,listener)方法在名为eventName的事件的侦听器数组的开头添加一个侦听器函数。不检查是否已添加侦听器。多次调用相同的eventName和listener会导致多次添加和调用listener。-eventEmitter.emit()方法允许将任意参数传递给侦听器函数。当从EventEmitter调用普通侦听器函数时,标准的this关键字设置为指向侦听器附加到的EventEmitter。//示例:constmyEE=newEventEmitter();myEE.on('foo',()=>console.log('a'));myEE.prependListener('foo',()=>console.log('b'));myEE.emit('foo');//print//b//aEventEmitter.once(event,listener)注册指定事件的单个监听器,即监听器只会在mostOnce监听器在被触发后立即被解散。server.once('connection',(stream)=>{console.log('Firstcall!');});EventEmitter.removeListener(event,listener)移除一个指定事件的监听器,该监听器必须是该事件监听器已经注册。(注意:removeListener最多只会从监听器数组中移除一个监听器实例。如果任何一个监听器被多次添加到指定事件名称的监听器数组,则必须多次调用removeListener以移除每个实例。)constcallback=(stream)=>{console.log('Connection!');};server.on('connection',callback);//...server.removeListener('connection',callback);EventEmitter.removeAllListeners([event])删除所有事件的所有监听器,如果指定了事件,则删除指定事件的所有监听器。constcallback=(stream)=>{console.log('connection!');};server.on('connection',callback);//...server.removeListener('connection',callback);error事件EventEmitter定义了一个特殊的事件error,它包含了“error”的语义,我们通常在遇到异常时会发出error事件。当发出错误时,EventEmitter指定如果没有响应的侦听器,Node.js将其视为异常,退出程序并打印调用堆栈。我们一般需要为发出错误事件的对象设置监听器,避免遇到错误导致整个程序崩溃。varevents=require('events');varemitter=newevents.EventEmitter();emitter.emit('error');继承EventEmitter大多数情况下,我们不会直接使用EventEmitter,而是在对象中继承,包括fs、http,只要支持事件响应的核心模块都是EventEmitter的子类即可。原因有二:具有一定实体功能的对象实现事件符合语义,事件的监听和发射应该是对象的方法。JavaScript的对象机制是基于原型的,支持部分多重继承。继承EventEmitter不会打乱对象原有的继承关系。