zoukankan      html  css  js  c++  java
  • NodeJS学习笔记(二).js

    1 Stream(数据流)
        当内存中无法一次装下需要处理的数据时,或者一边读取一边处理更加高效时,我们就需要用到
    数据流。NodeJS中通过各种 Stream 来提供对数据流的操作。

    (1)为数据来源创建一个只读数据流:
    var rs = fs.createReadStream(src);  // 从src读取文件, 返回一个新的可读流对象
    rs.on('data', function (chunk) {         //  触发'data'事件
     rs.pause();           //暂停触发'data'事件
     doSomething(chunk, function () {
      rs.resume();      //恢复触发'data'事件
     });
    });
    rs.on('end', function () {
     cleanUp();
    });

    (2)为数据目标创建一个只写数据流:
    var rs = fs.createReadStream(src);
    var ws = fs.createWriteStream(dst);
    rs.on('data', function (chunk) {
     if (ws.write(chunk) === false) {
     rs.pause();
     }
    });
    rs.on('end', function () {
     ws.end();
    });
    ws.on('drain', function () {    //发生在write()方法被调用并返回false之后。此事件被触发说明内核缓冲区已空,再次写入是安全的。
     rs.resume();
    });

    以上代码实现了数据从只读数据流到只写数据流的搬运,并包括了防爆仓控制。因为这种使用场
    景很多,例如上边的大文件拷贝程序,NodeJS直接提供了 .pipe 方法来做这件事情,其内部实
    现方式与上边的代码类似。

    2 File System(文件系统)

    NodeJS通过 fs 内置模块提供对文件的操作。fs 模块提供的API基本上可以分为以下三类:
    • 文件属性读写。
    其中常用的有 fs.stat、fs.chmod、fs.chown 等等。
    • 文件内容读写。
    其中常用的有 fs.readFile、fs.readdir、fs.writeFile、fs.mkdir 等等。
    • 底层文件操作。
    其中常用的有 fs.open、fs.read、fs.write、fs.close 等等。

    NodeJS最精华的异步IO模型在 fs 模块里有着充分的体现,例如上边提到的这些API都通过回调
    函数传递结果。以 fs.readFile 为例:
    fs.readFile(pathname, function (err, data) {
     if (err) {
     // Deal with error.
     } else {
     // Deal with data.
     }
    });
    如上边代码所示,基本上所有 fs 模块API的回调参数都有两个。第一个参数在有错误发生时等
    于异常对象,第二个参数始终用于返回API方法执行结果。
    此外,fs 模块的所有异步API都有对应的同步版本,用于无法使用异步操作时,或者同步操作
    更方便时的情况。同步API除了方法名的末尾多了一个 Sync 之外,异常对象与执行结果的传递
    方式也有相应变化。同样以 fs.readFileSync 为例:
    try {
     var data = fs.readFileSync(pathname);
     // Deal with data.
     } catch (err) {
     // Deal with error.
    }

    3 Path(路径)
    操作文件时难免不与文件路径打交道。NodeJS提供了 path 内置模块来简化路径相关操作,并提
    升代码可读性。
    (1) path.normalize:将传入的路径转换为标准路径,具体讲的话,除了解析路径中的 . 与 .. 外,还能去掉多余的斜
    杠。如果有程序需要使用路径作为某些数据的索引,但又允许用户随意输入路径时,就需要使用
    该方法保证路径的唯一性。以下是一个例子:
    var cache = {};
    function store(key, value) {
     cache[path.normalize(key)] = value;
    }
    store('foo/bar', 1);
    store('foo//baz//../bar', 2);
    console.log(cache); // => { "foo/bar": 2 }
    注意: 标准化之后的路径里的斜杠在Windows系统下是 ,而在*nix系统下是 /。如果想保证任何
    系统下都使用 / 作为路径分隔符的话,需要用 .replace(/\/g, '/') 再替换一下标准
    路径。

    (2) path.join:将传入的多个路径拼接为标准路径。该方法可避免手工拼接路径字符串的繁琐,并且能在不同系
    统下正确使用相应的路径分隔符。以下是一个例子:
    path.join('foo/', 'baz/', '../bar'); // => "foo/bar"

    (3) path.extname:该方法返回路径中的文件扩展名,即路径最低一级的目录中'.'字符后的任何字符串。如果路径最低一级的目录中没有'.' 或者只有'.',那么该方法返回一个空字符串。
    当我们需要根据不同文件扩展名做不同操作时,该方法就显得很好用。以下是一个例子:
    path.extname('foo/bar.js'); // => ".js"

    4 遍历目录
    遍历目录是操作文件时的一个常见需求。比如写一个程序,需要找到并处理指定目录下的所有JS
    文件时,就需要遍历整个目录。
    (1)遍历算法:
    目录是一个树状结构,在遍历时一般使用深度优先+先序遍历算法。深度优先,意味着到达一个
    节点后,首先接着遍历子节点而不是邻居节点。先序遍历,意味着首次到达了某节点就算遍历完
    成,而不是最后一次返回某节点才算数。因此使用这种遍历方式时,下边这棵树的遍历顺序是 A
    > B > D > E > C > F。
        A
       /
      B   C
     /   
    D   E   F

    (2) 同步遍历:
    function travel(dir, callback) {
     fs.readdirSync(dir).forEach(function (file) {
     var pathname = path.join(dir, file);
     if (fs.statSync(pathname).isDirectory()) {
      travel(pathname, callback);
     } else {
      callback(pathname);
         }
     });
    }

    (3) 异步遍历:
    function travel(dir, callback, finish) {
    fs.readdir(dir, function (err, files) {
    (function next(i) {
    if (i < files.length) {
    var pathname = path.join(dir, files[i]);
    fs.stat(pathname, function (err, stats) {
    if (stats.isDirectory()) {
    travel(pathname, callback, function () {
    next(i + 1);
    });
    } else {
    callback(pathname, function () {
    next(i + 1);
    });
    }
    });
    } else {
     finish && finish();
    }
    }(0));
    });
    }

    5 文本编码:

    (1) 常用的文本编码有 UTF8 和 GBK 两种,并且 UTF8 文件还可能带有BOM。在读取不同
    编码的文本文件时,需要将文件内容转换为JS使用的 UTF8 编码字符串后才能正常处理。
     可以根据文本文件头几个字节等于啥来判断文件是否包含BOM,以及使用哪种
    Unicode编码。但是,BOM字符虽然起到了标记文件编码的作用,其本身却不属于文件内容的一
    部分,如果读取文本文件时不去掉BOM,在某些使用场景下就会有问题。例如我们把几个JS文
    件合并成一个文件后,如果文件中间含有BOM字符,就会导致浏览器JS语法错误。因此,使用
    NodeJS读取文本文件时,一般需要去掉BOM。以下代码实现了识别和去除UTF8 BOM的
    功能:
    function readText(pathname) {
     var bin = fs.readFileSync(pathname);
     if (bin[0] === 0xEF && bin[1] === 0xBB && bin[2] === 0xBF) {
       bin = bin.slice(3);
     }
     return bin.toString('utf-8');
    }

    (2) GBK转UTF8
    NodeJS支持在读取文本文件时,或者在 Buffer 转换为字符串时指定文本编码,但遗憾的是,
    GBK编码不在NodeJS自身支持范围内。因此,一般我们借助 iconv-lite 这个三方包来转换编
    码。使用NPM下载该包后,我们可以按下边方式编写一个读取GBK文本文件的函数。

    var iconv = require('iconv-lite');
    function readGBKText(pathname) {
     var bin = fs.readFileSync(pathname);
     return iconv.decode(bin, 'gbk');
    }

    (3) 单字节编码:即使一个文本文件中有中文等字符,如果我们需要处理的字符仅在ASCII0~128范围
    内,比如除了注释和字符串以外的JS代码,我们就可以统一使用单字节编码来读取文件,不用关
    心文件的实际编码是GBK还是UTF8。
    1. GBK编码源文件内容:
    var foo = '中文';
    2. 对应字节:
    76 61 72 20 66 6F 6F 20 3D 20 27 D6 D0 CE C4 27 3B
    3. 使用单字节编码读取后得到的内容:
    var foo = '{乱码}{乱码}{乱码}{乱码}';
    4. 替换内容:
    var bar = '{乱码}{乱码}{乱码}{乱码}';
    5. 使用单字节编码保存后对应字节:
    76 61 72 20 62 61 72 20 3D 20 27 D6 D0 CE C4 27 3B
    6. 使用GBK编码读取后得到内容:
    var bar = '中文';
    这里的诀窍在于,不管大于0xEF的单个字节在单字节编码下被解析成什么乱码字符,使用同样
    的单字节编码保存这些乱码字符时,背后对应的字节保持不变。

    NodeJS中自带了一种 binary 编码可以用来实现这个方法,因此在下例中,我们使用这种编码
    来演示上例对应的代码该怎么写。
    function replace(pathname) {
     var str = fs.readFileSync(pathname, 'binary');
     str = str.replace('foo', 'bar');
     fs.writeFileSync(pathname, str, 'binary');
    }

    总结:
    • 学好文件操作,编写各种程序都不怕。
    • 如果不是很在意性能,fs 模块的同步API能让生活更加美好。
    • 需要对文件读写做到字节级别的精细控制时,请使用 fs 模块的文件底层操作API。
    • 不要使用拼接字符串的方式来处理路径,使用 path 模块。
    • 掌握好目录遍历和文件编码处理技巧,很实用。


          进程管理

    1 任何一个进程都有启动进程时使用的命令行参数,有标准输入标准输出,有运行权限,有运行环
    境和运行状态。在NodeJS中,可以通过 process 对象感知和控制NodeJS自身进程的方方面面。
    另外需要注意的是,process 不是内置模块,而是一个全局对象,因此在任何地方都可以直接
    使用。

    在NodeJS中可以通过 process.argv 获取命令行参数。但是比较意外的是,node 执行程序路
    径和主模块文件路径固定占据了 argv[0] 和 argv[1] 两个位置,而第一个命令行参数从
    argv[2] 开始。为了让 argv 使用起来更加自然,可以按照以下方式处理。
    function main(argv) {
    // ...
    }
    main(process.argv.slice(2));

    2 使用 child_process 模块可以创建和控制子进程。该模块提供的API中最核心的是 .spawn,
    其余API都是针对特定使用场景对它的进一步封装,算是一种语法糖。

    3 cluster 模块是对 child_process 模块的进一步封装,专用于解决单进程NodeJS Web服务器
    无法充分利用多核CPU的问题。使用该模块可以简化多进程服务器程序的开发,让每个核上运行
    一个工作进程,并统一通过主进程监听端口和分发请求。

    4 NodeJS程序的标准输入流(stdin)、一个标准输出流(stdout)、一个标准错误流(stderr)分别
    对应 process.stdin、process.stdout 和 process.stderr,第一个是只读数据流,后
    边两个是只写数据流,对它们的操作按照对数据流的操作方式即可。

    5
    > var t = new Date();   #=> Tue Mar 25 2014 08:41:27 GMT+0000 (UTC)

    > var k = new Date();  #=>  Tue Mar 25 2014 08:41:58 GMT+0000 (UTC)

    > k - t  #=> 30816  (毫秒)

  • 相关阅读:
    JS闭包
    css3 背景渐变
    css扩展技术:Less和Sass的区别
    HTML5 Canvas八大核心技术及其API用法
    HTML5新标签含义,用法及其与HTML4的区别
    当离散遇见连续
    素数测试
    概率采样问题
    二分查找及其变种
    C++与Java多态的区别
  • 原文地址:https://www.cnblogs.com/leaf526/p/3623808.html
Copyright © 2011-2022 走看看