zoukankan      html  css  js  c++  java
  • nodejs学习笔记

    Hello Fuck如下:

    console.log('Hello, Fuck you, NodeJs');
    
    # node Helloworld.js
    Hello, Fuck you, NodeJs
    

    事件:

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

    //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.js的基本组成部分,文件和模块是一一对应的,换言之,一个Node.js文件就是一个模块,这个文件可能是JavaScript代码,JSON或者编译过的c/c++扩展。

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

    创建,加载模块

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

    // 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,获得的模块都是同一个。

    覆盖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,仅仅是流控制和简单的消息解析,所有的高层功能都要通过它的接口来实现。

  • 相关阅读:
    POJ 1811 Prime Test(Miller-Rabin & Pollard-rho素数测试)
    HDU 3037 Saving Beans(Lucas定理模板题)
    HDU 5029 Relief grain(离线+线段树+启发式合并)(2014 ACM/ICPC Asia Regional Guangzhou Online)
    Java进阶(二)当我们说线程安全时,到底在说什么
    UML建模一般流程
    Spring事务管理(详解+实例)
    Spring 的 BeanPostProcessor接口实现
    Spring中ApplicationContext和beanfactory区别
    使用Maven Release插件发布项目
    tair与redis比较总结
  • 原文地址:https://www.cnblogs.com/mosmith/p/7464860.html
Copyright © 2011-2022 走看看