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

  • 相关阅读:
    start tag, end tag issues in IE7, particularly in xslt transformation
    用SandCastle为注释生成chm文档
    Firebug
    架构的重点
    Linux Shell常用技巧(十) 管道组合
    Linux JDK升级
    Linux Shell常用技巧(十二) Shell编程
    Packet Tracer 5.0实验(一) 交换机的基本配置与管理
    Linux Shell常用技巧(六) sort uniq tar split
    Linux Shell常用技巧(二) grep
  • 原文地址:https://www.cnblogs.com/mosmith/p/7464860.html
Copyright © 2020-2023  润新知