nodejs学习笔记

Hello Fuck以下:javascript

console.log('Hello, Fuck you, NodeJs');

# node Helloworld.js
Hello, Fuck you, NodeJs

事件:html

Node.js全部的异步i/o操做在完成时都会发送一个事件到事件队列,事件由EventEmitter对象来提供,前面提到的fs.readFile和http.createServer的回调函数都是经过EventEmitter来实现的。java

//event.js
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();

event.on('some_event', function() {
    console.log('some_event occurred.');
    setTimeout(function(){
        event.emit('some_event');
    },1000);
});

setTimeout(function() {
    event.emit('some_event');
},1000);

console.log('end?');

# node event.js 
end?
some_event occurred.
some_event occurred.
some_event occurred.
^C

 nodejs不停监测是否有活动的事件监听器好比i/o, timer等,一旦发现没有活动的事件监听器,nodejs进程将退出。node

 

模块c++

模块是Node.js的基本组成部分,文件和模块是一一对应的,换言之,一个Node.js文件就是一个模块,这个文件多是JavaScript代码,JSON或者编译过的c/c++扩展。git

前面章节的例子中,咱们曾经用到了相似于var http=require('http'),其中http就是一个核心模块,其内部是用c++来实现的,外部使用javascript来进行封装。经过require函数获取了这个模块以后,而后才能使用其中的对象。github

建立,加载模块web

在Node.js中,建立一个模块很是单简单,由于一个文件就是一个模块,咱们关注的问题仅仅在于如何在其它文件中获取这个模块,Node.js提供了exports和require两个对象,其中exports是模块公开的接口,而require用于从外部获取一个模块的接口,也就是获取返回的exports对象。express

// module.js
var name;
exports.setName=function(theName) {
    name=theName;
}

exports.sayHello=function() {
    console.log('Fuck you ' + name);
}


// getModule.js
var myModule=require('./module') //注意这里须要./前缀,由于是相对当前工做目录的。
myModule.setName('Mosmith');
myModule.sayHello();


# node getModule.js
Hello Mosmith

 

单次加载,这个有点相似于建立一个对象,但实际上和对象又有本质的区别,由于require不会重复加载模块,也就是说不管调用多少次require,得到的模块都是同一个。npm

覆盖exports,有时候咱们只是想将一个对象封闭到一个模块中,例如:

// module.js
function Hello() {
    var name;
    this.setName=function(_name) {
        this.name=_name;
    }
    this.sayHello=function() {
        console.log("Hello " + this.name);
    }
}
exports.Hello=Hello;

// override exports object
//module.exports=Hello;


// getModule.js
var Hello = require('./module.js').Hello;
var hello=new Hello();
hello.setName('Mosmith');
hello.sayHello();

须要注意的是,不能够经过对 exports 直接赋值代替对 module.exports 赋值。exports 实际上只是一个和 module.exports 指向同一个对象的变量,它自己会在模块执行结束后释放,但 module 不会,所以只能经过指定module.exports 来改变访问接口。

 

包是在模块的基础上更深一步的抽像,Node.js的包相似于c/c++的函数库或者Java/.Net的类库,它将某个独立的功能封装起来,用于发布,更新,依赖管理和版本控制,Node.js根据CommonJS规范实现了包机制,开发了npm来解决包的发布和获取需求。

Node.js的包是一个目录,其中包含了一个JSON格式的说明文件,package.json,严格符合CommonJS的包应该具有如下特征。

  1. package.json必须在包的顶层目录下
  2. 二进制文件应该在bin目录下
  3. JavaScript代码应该在lib目录下
  4. 文档应该在doc目录下
  5. 单元测试应该在test目录下

但Node.js对包的要求没有这么严格,只要顶层目录下面有package.json,并符合一些规范便可。但最好是符合规范。Node.js在调用某个包是,先会去检查包中的package.json中的main字段,将其做为包的接口模块,若是package.json或者main字段不存在,那么会尝试将寻找index.js或者index.node做为包的接口。

package.json是CommonJS规定用于描述包的文件,完整的包应该含有如下字段:

name:包的名称,必须是惟一的由小写字母,数字,和下划线组成,不能有空格。

description:包的简要说明。

version:符合语义化版本识别规范的字符串

maintainer:维护者数组,第个元素要包含name, email(可选), web(可选)

contributors:贡献者数组,格式与maintainer相同,包的做者应该是都数组的第一个元素。

bugs提交bug的地址。

licenses许可证数组,每一个元素要包含type的url

repositories仓库托管地址数组,每一个元素要包含type(仓库的类型好比git),url(仓库的地址)和path(相对于仓库的路径,可选)字段。

dependencies:包的依赖,一个关联数组,由包的名称和版本号组成。

下面是一下符合CommonJS规范的package.json示例:

{
    "name": "mypackage",
    "description": "Sample package for CommonJS. This package demonstrates the required elements of a CommonJS package.",
    "version": "0.7.0",
    "keywords": [
        "package",
        "example"
        ],

"maintainers": [
    {
        "name": "Bill Smith",
        "email": "bills@example.com",
    }
],

"contributors": [
    {
        "name": "BYVoid",
        "web": "http://www.byvoid.com/"
    }
],

"bugs": {
    "mail": "dev@example.com",
    "web": "http://www.example.com/bugs"
},

"licenses": [
    {
        "type": "GPLv2",
        "url": "http://www.example.org/licenses/gpl.html"
    }
],

"repositories": [
    {
    "type": "git",
    "url": "http://github.com/BYVoid/mypackage.git"
    }
],

"dependencies": {
    "webkit": "1.2",
    "ssl": {
        "gnutls": ["1.0", "2.0"],
        "openssl": "0.9.8"
        }
}
}

 

npm是Node.js的包管理工具,它已经成为Node.js包的标准发布平台。用于Node.js包的发布,传播,依赖控制。

npm install/i package_name好比要安装express

npm install express或者npm i express

在项目目录下运行npm install 将下载并安装package.json中的依赖。

同时npm还会自动解析其依赖,并获取express依赖的mime,mkdirp,qs的connect等。

注意:express4.x中将命令工具分离出来了,全部须要先装express-generator

在新建一个nodejs工程的时候咱们可使用npm init命令来进行初始化package.json,它的功能相似于maven archetype:generate

 

本地模块与全局模式

默认状况下npm会从http://npmjs.org搜索并下载包,并将包安装到当前目前的node_modules子目录下面。也就是本地模式。

另外npm能够以全局模式安装(使用-g参数),使用方法为:

npm install/i -g packageName

但须要注意的是,全局模式下可能会形成冲突,由于别的nodejs程序可能须要另外版本的包。

 

本地模式下npm不会注册环境变量,而全局模式下会注意环境变量,并将包安装到系统目录好比/usr/local/lib/node_modules,同时package.json文件中的bin字段包含的文件会被连接到/usr/local/bin。/usr/local/bin是在PATH环境变量中默认定义的,所以就能够直接在命令中运行像supervisor的模块了。但全局模式安装的package不能经过require来使用,由于require不会去搜索/usr/local/lib/node_modules目录。

 

建立全局连接

npm提供了一个npm link命令,它的功能是在本地包和全局包之间建立符号连接,咱们说过使用全局模式安装的包不能经过require来使用,但经过npm link命令能够绕过这个限制。好比:

npm link express

这里能够在node_modules子目录发现一个安装到全局的包的符号连接。但这个命令不能在windows下来使用。

 

调试。

node debug debug.js

node --debug[=port] script.js 而后在另外一个终端node debug localhost:debug_port或者使用IDE来进行远程调试。

 

全局对象

JavaScript中有一个特殊的对象,称为全局对象,它全部的属性能够在程序的任何地方访问,也就是说全局变量,在浏览器的JavaScript中,一般window是全局对象,而Node.js中的全局对象是global,全部的全局变量(除了global自己之外),都是global对象的属性。咱们在Node.js中可以直接访问到对象经过都是global的属性好比console, process等。

 

全局对象与全局变量

global最根本的做用是做为全局变量的宿主,按照ECMAScript的定义,知足如下条件的是全局变量。

  1. 最外层定义的变量。
  2. 全局对象的属性。
  3. 隐式定义的变量(未定义直接同赋值的变量)

当你定义一个全局变量时,这个变量同时也会成为全局对象的属性,反之亦然。须要注意的是,在 Node.js 中你不可能在最外层定义变量,由于全部用户代码都是属于当前模块的,而模块自己不是最外层上下文,提倡永远使用 var 定义变量以免引入全局变量,由于全局变量会污染命名空间,提升代码的耦合风险。

 

process

process是一个全局变量,即global对象的一个属性,它用于描述当前Node.js进程的状态,提供了一个与操做系统的简单接口。写一些本地命令行的程序的时候常常须要和它打交道的。

process.argv是命令行参数数组,第一个元素是node,第二个元素是脚本文件名,从第三个元素开始每一个元素是一个运行参数。

process.stdout是标准输出流,一般咱们使用的console.log向标准输出打印字符,而process.stdout.write()函数则提供更加底层的接口。

process.stdin是标准输入流,初始时它是被暂停的,想要从标准输入读取数据你必须恢复流,并手动编写流的事件响应函数。好比下面:

 

process.nextTick(callback)的功能是为事件循环设置一项任务,Node.js会在下一次事件循环时调用callback。Node.js 适合 I/O 密集型的应用,而不是计算密集型的应用,由于一个 Node.js 进程只有一个线程,所以在任什么时候刻都只有一个事件在执行。若是这个事件占用大量的 CPU 时间,执行事件循环中的下一个事件就须要等待好久,所以 Node.js 的一个编程原则就是尽可能缩短每一个事件的执行时间。 process.nextTick() 提供了一个这样的工具,能够把复杂的工做拆散,变成一个个较小的事件。

process.stdin.on('data', function(data) {
    process.nextTick(function() {
        process.stdout.write('do something very time-consuming');
        process.stdout.write(data);
    });
});

须要setTimeout也能够达到相似的做用,但setTimeout效率很低,回调不能被及时执行。

除了上面几个比较经常使用的成员,除些以后有process.platform,process.pid,process.execPath,process.memoryUsage(),以及POSIX进程信号响应机制。

 

console

console用于提供控制台标准/错误输出:

console.log()向标准输出流打印字符并以换行符结束。console.log接受若干个参数,若是只有一个参数,则输出这个参数的字符串形式,若是有多个参数,则相似于c语言的printf命令的格式化输出。

console.log("Helloworld"); => Helloworld
console.log("Helloworld%s"); => Helloworld%s console.log("Helloworld %s", Mosmith); => Helloworld Mosmith

console.error()与console.log相同,只不过console.error()向标准错误流输出。

console.trace()用于向标准错误流输出当前的调用栈。

 

经常使用工具util

util是Node.js的核心模块,提供经常使用函数的集合,用于弥补核心JavaScript的功能过于精简的不足。

util.inherits(constructor, superConstructor)是一个实现对象间原型继承的函数,JavaScript的面向对象我是基于原型的,与常见的基于类不一样,JavaScript并无提供对象继承的语言级别特性,而是经过原型复制来实现的,具体细节咱们在附录A中说明,这里咱们只介绍util.inerits的用法,示例以下:

var util=require('util')

function Base() {
  this.name='base';
    this.base=1991;
    this.sayHello = function() {
        console.log('Hello ' + this.name);
    }
}

Base.prototype.showName=function() {
    console.log(this.name);
}

function Sub() {
    this.name='sub';
}

util.inherits(Sub,Base);

var objBase=new Base();
objBase.showName();
objBase.sayHello();
console.log(objBase);

var objSub=new Sub();
objSub.showName();
// This is undefined in Sub
// objSub.sayHello();
console.log(objSub);

 

util.inspect

util.inspect(object, [showHidden],[depth],[colors])是一个将任意对象转换为字符串的方法,一般用于调试和错误输出,它至少一个参数object,即要转换的对象,showHidden是一个可选的参数,若是值为true,将会输出更多的参数,depth表示最大的递归层数,若是对象很复杂,你能够指定层数以控制输出信息的多少,默认状况下递归两层,为null的状况下则不限层数。

 

事件驱动events

events是Node.js的最重要的模块,Node.js自己就是依赖于event实现事件驱动的,而它提供了惟一的接口。events模块不只仅用于用户代码与Node.js下层事件循环的交互,还几乎被全部的模块依赖。

 

事件发射器

events模块只提供了一个对象,events.EventEmitter, EventEmitter的核心就是事件发射与事件监听器功能的封装。EventEmitter的每个事件由一个事件和若干个参数组成,事件名是一个字符串,一般表达必定的语义,对于每一个事件,EventEmitter支持若干个事件监听器,当事件发射时,注册到这个事件的事件监听器将被依次调用,事件参数做为回调函数传递。

// eventEmitterTest.js
var
events=require('events'); var emitter=new events.EventEmitter(); emitter.on('someEvent',function(arg1,arg2){ console.log('listener1 invoked',arg1,arg2); }); emitter.on('someEvent', function(arg1,arg2) { console.log('listener2 invoked',arg1,arg2); }); emitter.emit('someEvent','argument1',2017); $ node eventEmitterTest.js listener1 invoked argument1 2017 listener2 invoked argument1 2017
  • EventEmitter.on(event, listener) 为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数 listener 。
  • EventEmitter.emit(event, [arg1], [arg2], [...]) 发射event事件,传递若干可选参数到事件监听器的参数表。
  • EventEmitter.once(event, listener) 为指定事件注册一个单次监听器,即监听器最多只会触发一次,触发后马上解除该监听器。
  • EventEmitter.removeListener(event, listener) 移除指定事件的某个监听器, listener 必须是该事件已经注册过的监听器。
  • EventEmitter.removeAllListeners([event]) 移除全部事件的全部监听器,若是指定 event ,则移除指定事件的全部监听器

error事件

EventEmitter定义了一个特殊的事件error,它包含了’错误‘的语义,咱们在遇到异常的时候一般会发射error事件。当error被发射时,EventEmitter规定若是没有响应的监听器,那么Node.js会将它看成异常,退出程序并打印调用栈。所以咱们通常要为会发射error事件的对象设置监听器,避免遇到错误后整个程序崩溃。好比:

var events = require('events');
var emitter = new events.EventEmitter();
emitter.emit('error');

运行时会显示如下错误:
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Uncaught, unspecified 'error' event.
at EventEmitter.emit (events.js:50:15)
at Object.<anonymous> (/home/byvoid/error.js:5:9)
at Module._compile (module.js:441:26)
at Object..js (module.js:459:10)
at Module.load (module.js:348:31)
at Function._load (module.js:308:12)
at Array.0 (module.js:479:10)
at EventEmitter._tickCallback (node.js:192:40)

 

继承EventEmitter

大多数时候咱们不会直接使用 EventEmitter ,而是在对象中继承它。包括 fs 、 net 、http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。为何要这样作呢?缘由有两点。首先,具备某个实体功能的对象实现事件符合语义,事件的监听和发射应该是一个对象的方法。其次 JavaScript 的对象机制是基于原型的,支持部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。

 

文件系统fs

fs模块是文件操做的封闭,它提供了文件的读取,写入,改名,删除,遍历目录,连接等POSIX文件操做,与其它模块不一样的是,fs模块全部的操做都有同步和异步两个版本,例如读取文件内容的函数有异步的fs.readFile()也有同步的fs.readFileSync

fs.readFile(filename, [encoding], [callback(err,data)])是最简单的读取文件的函数,它接受一个必选的参数filename,若是不提供encoding,那么将以二进制方式打开,若是指定了encoding,data是一个解析后的字符串。

fs.readFIleSync是同步版本,它没有callback参数,data经过返回值获取,而错误则须要经过try catch来进行捕捉处理。

fs.open(path, flag, [mode], [callback(err,fd])]是POSIX open函数的封装,与c和fopen相似,它两个必选参数,path为文件路径,flag是表示以什么方式打开文件,mode参数用于建立文件时给文件指定权限,默认是0666,回调函数将传递一个错误参数,以及一个文件描述符fd

fs.read(fd,buffer, offset, length, position, callback(err, byteRead, buffer)]是POSIX read函数的封闭,相比于fs.readFile, 它提供了更加底层的接口,fs.read功能是从指定的文件描述符fd中读取数据并写入buffer指定的缓冲区对象,offset是buffer的写入偏移量,length是要从文件中读取的字节数,position是文件读取的起始位置,若是position的值为null,则会从当前文件指针的位置读取,回调函数传递byteRead和buffer,分别表示读取的字节数和缓冲区对象。

其中有两个全局变量:

__dirname:全局变量,存储的是文件所在的文件目录
__filename:全局变量,存储的是文件名

 

http模块

Node.js标准库里面提供了http模块,其中Node.js 标准库提供了 http 模块,其中封装了一个高效的 HTTP 服务器和一个简易的HTTP 客户端。 http.Server 是一个基于事件的 HTTP 服务器,它的核心由 Node.js 下层 C++部分实现,而接口由 JavaScript 封装,兼顾了高性能与简易性。 http.request 则是一个HTTP 客户端工具,用于向 HTTP 服务器发起请求,例如实现 Pingback 1 或者内容抓取

 

http.Server

http.Server是http模块中的HTTP服务器对象,用Node.js作的全部基于HTTP协议的系统如网站,社交应用,甚于代理服务器,都是基于http.Server来实现的,它提供了一套封装级别很低的API,仅仅是流控制和简单的消息解析,全部的高层功能都要经过它的接口来实现。

相关文章
相关标签/搜索