NodeJs 入门

什么是 NodeJS?

简单的来说 Node.js 就是运行在服务器端的 Javascript。
Node.js 是一个基于 Chrome Javascript 运行时建立的一个平台。
Node.js 是一个事件驱动 I/O 服务端 Javascript 环境,基于 Google 的 V8 引擎,V8 引擎执行 Javascript 的速度非常快,性能非常好。

REPL(交互式解释器)

REPL -> Read Eval Print Loop, 表示一个电脑环境,类似系统的终端,可以直接输入命令

  • R 读取用户输入,解析输入的 Javascript 数据解构并存储在内存中。
  • E 执行输入的数据解构
  • P 打印,输出结果
  • L 循环,循环操作以上步骤直到用户两次按下 ctrl+c 按钮退出。
$ node
> 1 + 4
5

REPL 命令

  • ctrl + c 退出当前终端
  • ctrl + c 按两次 退出 Node PEPL === .exit
  • .help 列出使用命令

回调函数

Node.js 异步编程的直接就是回调。

// input.text
第一次创建input.text;
// main.js
// 同步,会阻塞
let fs = require("fs");
let data = fs.readFileSync("./input.text");

console.log(data.toString());
console.log("end");

// 输出结果
第一次创建input.text;
end;
// main.js
// 异步,不会阻塞
fs.readFile("./input.text", function(error, data) {
  if (error) return console.log(error);
  data && console.log(data.toString());
});

// 输出结果
end;
第一次创建input.text;

EventEmitter

events 模块只提供了一个对象: events.EventEmitter。
EventEmitter 的核心就是事件触发与事件监听功能的封装。

let events = require("events");

let eventEmitter = new events.EventEmitter();

// 监听notice 事件
eventEmitter.on("notice", function(args) {
  console.log("监听了notice," + args);
});

// 触发notice事件
setTimeout(function() {
  eventEmitter.emit("notice", "我被触发了!");
}, 500);
方法描述
addListener (event, listener)为指定事件添加一个监听器到监听器数组的尾部
on (event, listener)为指定事件注册一个监听器,接收一个字符串 event 和 一个回调函数
once (event, listener)为指定事件注册一个单词监听器,即监听器最多只会触发一次,触发后立刻解除该监听器
removeListener (event, listener)移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器
removeAllListeners ([event])移除所有事件的所有监听器,如果指定事件,则移除指定事件下的所有监听器
setMaxListeners (n)同一个事件,最大监听器数量,默认超过 10 个就会输出警告信息
listeners (event)返回指定事件的监听器数组
listenerCount (event)返回指定事件的监听器数量
emit(event, [arg1], [arg2], [...])按监听器的顺序执行执行每个监听器,如果事件有注册监听返回 true,否则返回 false
let events = require("events");

let eventEmitter = new events.EventEmitter();

// 设置最大监听器的数量,比如notice3监听器超过3个,就会报错
eventEmitter.setMaxListeners(3);

eventEmitter.on("notice", function(args) {
  console.log("监听了notice方法0000," + args);
});

eventEmitter.on("notice1", function(args) {
  console.log("监听了notice方法1111," + args);
});

eventEmitter.on("notice2", function(args) {
  console.log("监听了notice方法22222," + args);
});

// 为notice3 注册4个监听器
eventEmitter.on("notice3", function(args) {
  console.log("监听了notice方法333333," + args);
});

eventEmitter.on("notice3", function(args) {
  console.log("监听了notice方法333333------1," + args);
});

eventEmitter.on("notice3", function(args) {
  console.log("监听了notice方法333333------2," + args);
});

eventEmitter.on("notice3", function(args) {
  console.log("监听了notice方法333333------3," + args);
});

eventEmitter.on("notice4", function(...args) {
  console.log(`传递的参数有: ${args}`);
});

setTimeout(function() {
  eventEmitter.emit("notice2", "我被触发了!");
  // 移除所有监听器
  eventEmitter.removeAllListeners();

  // 移除notice3下的所有监听器
  eventEmitter.removeAllListeners("notice3", () => {
    console.log("移除notice3下所有的监听器");
  });

  setTimeout(() => {
    eventEmitter.emit("notice3", "我被触发了!");
    eventEmitter.emit("notice2", "我被触发了!+1 ");

    // 传递多个参数
    eventEmitter.emit("notice4", 1, 2, 3, 4, 5, 6);
  }, 1000);
}, 500);

// 输出 notice3事件下的监听器数组
console.log(eventEmitter.listeners("notice3"));

// 输出 notice3事件下的监听器数量
console.log(eventEmitter.listenerCount("notice3"));

Buffer 缓冲区

Javascript 语言自身只有字符串数据类型,没有二进制数据类型。

但在处理像 TCP 流或文件流时,必须使用到二进制数据。因此在 Node.js 中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。

在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库。 Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 Node.js 中处理 I/O 操作中移动的数据时,就有可能使用 Buffer 库。

温馨提示

在 v6.0 之前创建 Buffer 对象直接使用 new Buffer()构造函数来创建对象实例,但是 Buffer 对内存的操作权限操作很大,可以直接捕获一些敏感信息。所以在 v6.0 以后,官方文档建议使用 Buffer.from() 去操作 Buffer 对象。

Buffer 与 字符编码

Buffer 实例一般用于表示编码字符的序列,比如 UTF-8、UCS2、Base64、或十六进制编码的数据。通过使用显式的字符编码,就可以在 Buffer 实例与普通的 Javascript 字符串之间进行相互转换。

目前支持的字符编码有:

  • ascii: 仅支持 7 位 ASCII 数据。如果设置去掉高位的话,这种编码是非常快的
  • utf8: 多字节编码的 Unicode 字符。许多网页和其他文档格式都使用 UTF-8
  • utf16le: 2 或 4 个字节,小字节序编码的 Unicode 字符。支持代理对(U+10000 至 U+10FFFF)
  • ucs2: utf16le 的别名
  • base64: Base64 编码
  • latin1: 一种把 Buffer 编码成一字节编码的字符串的方式
  • binary: latin1 的别名
  • hex: 将每个字节编码为两个十六进制字符
const buf = Buffer.from("runoob", "ascii");

console.log(buf.toString("hex"));
console.log(buf.toString("base64"));

Stream 流

Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对 http 服务器发起请求的 request 对象就是一个 Stream,还有 stdout(标准输出)。

Node.js,Stream 有四种流类型:

  • Readable - 可读操作

  • Writable - 可写操作

  • Duplex - 可读可写操作

  • Transform - 操作被写入数据,然后读出结果


所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:

  • data - 当有数据可读时触发。

  • end - 没有更多的数据可读时触发。

  • error - 在接收和写入过程中发生错误时触发。

  • finish - 所有数据已被写入到底层系统时触发。

从流中读取数据

const fs = require("fs");
let data = "";

// 创建可读流
let readerStream = fs.createReadStream("./input.txt");

// 设置编码为utf8
readerStream.setEncoding("UTF-8");

// 处理流事件 data、end 、error、 finish
readerStream.on("data", function(chunk) {
  data += chunk;
});

readerStream.on("end", function() {
  console.log("end 输出结果:" + data);
});

readerStream.on("error", function(err) {
  console.log("error 输出结果:" + err.stack);
});

console.log("程序执行完毕");

执行结果
程序执行完毕
end 输出结果:读取文件内容,这里是内容。

写入数据

const fs = require("fs");
let data = "这里是写入的内容";

// 创建一个可以写入的流,写入到文件 output.txt 中
let writeStream = fs.createWriteStream("./output.txt");

// 使用utf8写入数据
writeStream.write(data, "UTF-8");

// 标记文件末尾
writeStream.end();

// 处理流事件 data、end 、error、 finish
writeStream.on("end", function() {
  console.log("end 输出结果:" + data);
});

writeStream.on("error", function(err) {
  console.log("error 输出结果:" + err.stack);
});

writeStream.on("finish", function() {
  console.log("写入完成");
});

console.log("程序执行完毕");

执行结果
程序执行完毕
写入完成

可以看到输出了个 output.txt 文件,文件中被写入了这里是写入的内容。

管道流

管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。相当于两个杯子中加加了个吸管,可以从高处流向低处。

操作一下,现在将通过读取一个文件内容写入到另外个文件中

const fs = require("fs");

// 创建一个可读流
const readStream = fs.createReadStream("./input.txt");

// 创建一个可写流
const writeStream = fs.createWriteStream("./output.txt");

// 管道读写操作
// 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中
readStream.pipe(writeStream);

console.log("程序执行完毕");

执行结果
程序执行完毕

可以看到 output.txt 中被写入了读取文件内容,这里是内容。

链式流

链式是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作。 相当于用多个吸管连接起来。

接下来我们就是用管道和链式来压缩和解压文件。

const fs = require("fs");
const zlib = require("zlib");

// 压缩 input.txt 文件为 input.txt.gz
fs.createReadStream("./input.txt")
  .pipe(zlib.createGzip())
  .pipe(fs.createWriteStream("input.txt.gz"));

console.log("文件压缩成功");

解压操作也是如此,先读取文件再写入。

模块系统

为了让 Node.js 的文件可以互相调用,Node.js 提供了简单的模块系统。

一个 Node.js 文件就是一个模块。

创建模块

// main.js
let hello = require("./hello");
hello.world();

// hello.js
exports.world = function() {
  console.log("Hello Nodejs");
};

Node.js 提供了 exportsrequire 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。

hello.js 通过 exports 对象把 world 作为模块的访问接口,直接可以访问 hello.js 中 exports 对象的成员函数了。

也可以把对象封装到模块中,如

// hello.js
function Hello() {
  let name;
  this.setName = function(opts) {
    name = opts;
  };
  this.sayHello = function() {
    console.log("Hello " + name);
  };
}

module.exports = Hello;

// main.js
let Hello = require("./hello");

let hello = new Hello();
hello.setName("kk");
hello.sayHello();

模块接口的唯一变化是使用了 module.exports = Hello; 代替了 exports.world = function(){}
在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的 exports


exports 与 module.exports 区别

exports 其实是 module.exports 的简写, exports === module.exports === {}, 都是指向同一个内存地址。

  • require 引入的其实是module.exports导出的, exports 只是 module.exports 决定导出一个对象时的快捷方式。
  • 一个模块文件中可以有多个 exports 输出,但是只能有一个 module.exports 输出
function Hello() {
  var name;
  this.setName = function(argName) {
    name = argName;
  };
  this.sayHello = function() {
    console.log("Hello " + name);
  };
}

function Test() {
  console.log("test");
}

// 返回的是模块对象本身,返回的是一个类
module.exports = Hello;

// 导出多个
module.exports = {
  Hello,
  Test
};

// 返回的是模块函数
exports.Hello = Hello;
// 导出多个
exports.Test = Test;

扩展一下: 区分 ES6 Module

  • 模块引入: import
  • 模块导出: export || export default
  1. 在一个文件或模块中,export 可以有多个,export default 只能向外暴露一次
  2. 通过 export 方式导出,是一个对象,需要解构。export default, 实际上是输出一个叫 default 的变量,用于储存。
  3. Node 不支持 ES6 module 特性,需要用 babel 编译
// export
export const name = "kk";
export function fun() {
  console.log("123");
}

import { name, fun } from "./export";
console.log(name); // 'kk'
console.log(fun); // fun: [Function: fun]
fun(); // '123'

// export default
function fun() {
  console.log("123");
}
export default fun;

// 系统允许为它取任意变量名
import fun from "./export-default";
console.log(fun); // fun: [Function: fun]
fun(); // '123'

再看看这些

// test.js
const options = {
  name: "kk",
  age: 16
};

// module.exports 导出
module.exports = options;
// main.js
const opt = require("./test.js");

// exports.default 导出
exports.default = options;
const opt = require("./test.js").default;

// export default 导出
export default options;
const opt = require("./test.js").default;

// export 导出
export const options = {
  name: "kk",
  age: 16
};
// 需要解构
const { options } = require("./test.js");
Node浏览器
模块规范CommonJSES6
导出modules.exports, exportsexport, export default
导入requireimport, require

服务端模块

Node.js 中自带一个叫做 http 的模块。

const http = require('http');

http.createServer(...);

函数

跟函数式编程类似,将函数作为形参。可以先定义一个函数,再进行传递。也可以在传递参数的地方直接定义函数。

function say(value) {
  console.log(value);
}

function execute(fun, value) {
  fun(value);
}

execute(say, "Hello Nodejs");

用函数方式启动 HTTP 服务器

// 普通方式
const http = require("http");

http
  .createServer(function(req, resp) {
    resp.writeHead(200, { "Content-type": "text/plan" });
    resp.write("Hello Nodejs");
    resp.end();
  })
  .listen(8888);

// 通过传递函数
function onRequest(req, resp) {
  resp.writeHead(200, { "Content-type": "text/plan" });
  resp.write("Hello Nodejs");
  resp.end();
}

http.createServer(onRequest).listen(8888);

路由

为路由提供请求的 URL 和 其他需要的 GET 及 POST 参数,随后路由需要根据这些数据来执行相应的代码。

// index.js
let router = require("./router");
let server = require("./server");

server.start(router.route);

// router.js
function route(pathname, response) {
  // console.log('About to route a request for ' + pathname);
  if (pathname === "/") {
    response.writeHead("200", { "Content-Type": "text/plain" });
    response.write("200, Hello Nodejs");
    response.end();
  } else if (pathname === "/about") {
    response.end("about");
  } else {
    response.end("404");
  }
}

exports.route = route;

// server.js
let http = require("http");
let url = require("url");

function start(route) {
  function onRequest(request, response) {
    // console.log(url.parse(request.url));
    let pathname = url.parse(request.url).pathname;
    // console.log(`Request for ${pathname} received.`);

    route(pathname, response);
  }

  http.createServer(onRequest).listen(8900);
  console.log("服务启动");
}

exports.start = start;

执行 index.js 文件,就可以看了。当路由切换到 about, 页面会输出 about。

全局对象

Javascript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。

在浏览器 Javascript 中,window、self 是全局对象,而在 Node.js 中全局对象是 global, 所有全局变量(即除了 global 本身以外)都是 global 对象的属性。

全局对象与全局变量

global 最根本的作用是作为全局变量的宿主。

在 Node.js 中你不可能在最外层定义变量,因为所有用户代码都是属于当前模块的,而模块本身不是最外层上下文。

注意

最好不要使用 var 定义变量,避免引入全局变量。因为群居变量会污染命名空间,提高代码的耦合风险!

__filename

__filename 表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径。

console.log(__filename);

// /Users/kk/kk/project/learn-notes/src/node/main.js

__dirname

__dirname 表示当前执行脚本所在的目录。

console.log(__dirname);

// /Users/kk/kk/project/learn-notes/src/node/

setTimeout(cb, ms) && clearTimeout(t)

全局函数在指定的毫秒(ms)数后执行指定函数(cb)

setTimeout() 只执行一次指定函数。 返回一个代表定时器的句柄值。

clearTimeout(t) 全局函数用于停止一个之前通过 setTimeout() 创建的定时器。

用法跟跟 js 相同

let t = setTimeout(function() {
  console.log("Hello Nodejs");
}, 200);

clearTimeout(t);

setInterval(cb, ms)

setInterval(cb, ms) 全局函数在指定的毫秒数后执行指定函数,返回一个代表定时器的句柄值。

可以使用 clearInterval(t) 函数来清除定时器。

setInterval() 会不停的调用函数,直到 clearInterval() 被调用或者窗口被关闭。

let t = setInterval(function() {
  console.log("Hello Nodejs");
}, 200);

以上程序每隔两秒就会输出一次"Hello Nodejs",且会永久执行下去,直到中断它。(ctrl + c) 或者调用 clearInterval()

let count = 0;
const t = setInterval(function() {
  if (count === 10) {
    clearInterval(t);
  } else {
    count++;
    console.log("Hello Nodejs");
  }
}, 200);

console

console 用于控制台标准输出,是调试工具,后面逐渐成为浏览器的实施标准。

常用方法:

方法描述
log([data][,...])向标准输出流打印字符并以换行符结束
info([data][,...])输出消息性信息
error([data][,...])输出错误消息
warn([data][,...])输出警告信息
dir(obj[,options])用来对一个对象进行检查,并以易于阅读和打印的格式显示
time(label)输出时间,表示计时开始
timeEnd(label)输出结束时间,表示计时结束
trace(message[,...])当前执行的代码在堆栈中的调用路径
assert(value[,message][,...])用于判断某个表达式或变量是否为真,接收两个参数,第一个参数是表达式,第二个参数是字符串。只有当第一个参数为 false,才会输出第二个参数,否则不会有任何结果。
// 这两个方法均使用一个参数,参数值可以为任何字符串,但是这两个方法所使用的参数字符串必须相同,才能正确地统计出开始时间与结束时间之间所经过的毫秒数。
console.time("small loop");
for (let i = 0; i < 1000; i++) {
  console.log(i);
}
console.timeEnd("small loop");
// small loop: 47.735ms

function foo() {
  function bar() {
    console.trace();
  }
  bar();
}
foo();

console.assert(1 + 1 > 2, "false, 1+1不大于2");
console.assert(false, "the word is %s", "foo");

const errorMsg = "the # is not even";
for (let number = 2; number <= 5; number += 1) {
  console.log("the # is " + number);
  console.assert(number % 2 === 0, { number, errorMsg });
}

process

process 是一个全局变量,即 global 对象的属性。

用于描述当前 Node.js 进程状态的对象,提供了一个与操作系统的简单接口。

事件描述
exit当进程准备退出时触发
beforeExit当 node 清空事件循环,并且没有其他安排时触发这个事件。
uncaughtException当一个异常冒泡回到事件循环,触发这个事件。这个给异常添加了监视器,默认的操作就不会发生。
Signal当进程接收到信息时就触发。
process.on("exit", function(code) {
  // 以下代码永远不会执行
  setTimeout(function() {
    console.log("该代码不会执行");
  }, 0);

  console.log("退出状态码: " + code);
});
console.log("程序执行完毕!");

程序执行完毕!
退出状态码: 0

一些常用的属性

// 输出到终端
process.stdout.write("Hello Nodejs" + "\n");

// 通过参数读取
console.log(process.argv);
process.argv.forEach(function(item, index, array) {
  console.log(index + ":" + item);
});

// 获取执行路径
console.log(process.execPath);

// 输出当前目录
console.log(process.cwd());

// 平台信息
console.log(process.platform);

// 版本
console.log(process.version);

// 版本信息
console.log(process.versions);

// 输出内存使用情况
console.log(process.memoryUsage());

常用工具 util

util 是 Nodejs 的核心模块,提供常用函数的集合,用于弥补核心 Javascript 的功能过于精简的不足。

const util = require("util");

util.callbackify

util.callbackify(original) 将 async 移步函数(或者一个返回值为 promise 的函数),转换成异常优先的回调等个的函数。如(err, value) => ... 回调作为最后一个参数。

// 异步函数
const util = require("util");

async function fn() {
  return "Hello Nodejs";
}

const callbackFunction = util.callbackify(fn);
callbackFunction((err, res) => {
  if (err) throw err;
  console.log(res);
});

// Promise
function fn() {
  return Promise.resolve("Hello Nodejs");
  // return Promise.reject('错误信息');
}

回调函数是异步执行的,并且有异常堆栈错误追踪。如果回调函数抛出一个异常,进程会触发一个'uncaughtException' 异常。如果没有被捕获,那么进程将会退出。 另外,null 在回调函数中作为一个参数有特殊的意义,如果回调函数的首个参数为 Promise 拒绝的原因且带有返回值,且值可以转换成布尔值 false, 这个值会被封装在 Error 对象里,可以通过 reason 属性获取。

function fn() {
  return Promise.reject(null);
}

const callbackFunction = util.callbackify(fn);
callbackFunction((err, res) => {
  // 当 Promise 被以 `null` 拒绝时,它被包装为 Error 并且原始值存储在 `reason` 中。
  if (err && err.hasOwnProperty("reason") && err.reason === null) {
    console.log(err);
  }
});

// 输出err错误信息

util.inherits

util.inherits(constructor, superConstructor) 是一个实现对象间原型继承的函数。

Javascript 的面向对象特性是基于原型的,与常见的基于类的不同。Javascript 没有提供对象继承的语言级别特性,而是通过原型复制来实现的。

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

Base.prototype.showName = function() {
  console.log(this.name);
};
Base.prototype.diyString = "自定义属性";

function Sub() {
  this.name = "Sub";
}

// 定义一个基础对象Base 和 一个继承自Base的Sub
util.inherits(Sub, Base);

let objBase = new Base();
console.log(objBase.name);
console.log(objBase.base);
console.log(objBase.sayHello());
console.log(objBase.showName());
console.log(objBase.diyString);
console.log(objBase);

let objSub = new Sub();
console.log(objSub.name);
console.log(objSub.base);
// 去掉这行注释,将会报错,Sub 中并没有 sayHello 函数
// console.log(objSub.sayHello());
console.log(objSub.showName());
console.log(objSub.diyString);
console.log(objSub);

TIP

Sub 仅仅继承了 Base 在原型中定义的函数,而构造函数内部创造的 base 属性和 sayHello 函数都没有被 Sub 继承
util.inherits() 只会继承原型中的属性。

util.inspect

util.inspect(object, [showHidden], [depth], [colors]) 是一个将任意对象转换成字符串的方法。通常用于调试和错误的输出。

  • object, 即要转换的对象
  • showHidden, 可选参数,为 true,表示会输出更多隐藏信息
  • depth,表示最大递归层数,默认为 2,指定为 null 表示将不限递归层数完整遍历对象。
  • colors, 为 true, 表示输出格式为 ANSI 颜色编码

util.inspect() 并不会简单地直接把对象转换成字符串,即使该对象定义了 toString 方法也不会调用。

console.log (util.inspect(objBase));
console.log (util.inspect(objBase, true));

// 输出结果
Base { name: 'Base', base: 2020, sayHello: [Function] }
Base {
  name: 'Base',
  base: 2020,
  sayHello:
   { [Function]
     [length]: 0,
     [name]: '',
     [arguments]: null,
     [caller]: null,
     [prototype]: { [constructor]: [Circular] } } }

util.types.isRegExp(object)

如果形参 object 是一个正则表达式,则返回 true,否则返回 false。

util.isRegExp(object) v4.0.0 已被弃用

console.log(util.types.isRegExp(/some regexp/));
console.log(util.types.isRegExp(new RegExp("test regexp")));
console.log(util.types.isRegExp({}));

// 输出结果
true;
true;
false;

util.types.isDate(object)

如果形参 object 是一个日期,则返回 true,否则返回 false。 只支持 Date 对象。

util.isDate(object) v4.0.0 已被弃用

console.log(util.types.isDate(new Date()));
console.log(util.types.isDate(Date()));
console.log(util.types.isDate({}));
console.log(util.types.isDate("2020/07/02"));
console.log(util.types.isDate("1593619200000"));

// 输出结果
true;
false;
false;
false;
false;

util.isArray(object) 弃用

如果形参 object 是一个数组,则返回 true,否则返回 false。

v4.0.0 弃用,替代方法 Array.isArray(object)

console.log(Array.isArray([]));
console.log(Array.isArray(new Array()));
console.log(Array.isArray({}));
console.log(Array.isArray("123"));
console.log(Array.isArray(123));
console.log(Array.isArray(true));

// 输出结果
true;
true;
false;
false;
false;
false;

文件系统

Node.js 提供一组类似 UNIX (POSIX)标准的文件操作 API。 fs 模块。

const fs = require("fs");

异步 && 同步

fs 模块中的方法均有异步和同步版本,如读取文件内容的函数,异步 -> fs.readFile() ,同步 -> fs.readFileSync().

异步方法函数,最后一个参数为回调函数 cb, 回调函数的第一个参数包含了错误信息 error。

建议大家都使用异步方法,比起同步,异步性能更高,速度更快,不会阻塞。

let fs = require('fs');

// 异步读取文件
fs.readFile('./input1.txt', (error, data) => {
  if (error) {
    console.log(error);
    return;
  }
  console.log(data.toString());
});

console.log('程序执行完毕');

//  输出结果
程序执行完毕
读取文件内容,这里是内容。


// 同步读取文件
let data = fs.readFileSync('./input.txt', 'utf-8');
console.log(data.toString());

console.log('程序执行完毕');

//  输出结果
读取文件内容,这里是内容。
程序执行完毕

打开文件

以下为异步模式下打开文件的语法格式:

fs.open(path, flags[, mode], callback);
参数描述
path文件路径
flags文件打开的行为
mode文件模式(权限)
callback回调函数,带有两个参数 callback(error, fd)
Flag描述
r以读取模式打开文件,如果文件不存在抛出异常
r+以读写模式打开文件,如果文件不存在抛出异常
rs以同步的方式读取文件
rs+以同步的方式读取和写入文件
w以写入模式打开文件,如果文件不存在则创建
wx类似'w', 但是如果文件路径存在,则文件读写失败
w+以读写模式打开文件,如果文件不存在则创建
wx+类似'w+', 但是如果文件路径存在,则文件读写失败
a以追加模式打开文件,如果文件不存在则创建
ax类似'a', 但是如果文件路径存在,则文件追加失败
a+以读取追加模式打开文件,如果文件不存在则创建
aw+类似'a+',但是如果文件路径存在,则文件追加失败
// 异步打开文件
console.log("准备打开文件");
fs.open("./input.txt", "w+", function(err, fd) {
  if (err) {
    return console.log(err);
  }
  // 文件描述符
  console.log(fd);
  console.log("文件打开成功");
});

// 输出结果
准备打开文件;
20;
文件打开成功;

写入文件

以下为异步模式下打开文件的语法格式:

fs.writeFile(file, data[, options], callback)

callback 只包含 error, callback(error)

console.log('准备写入文件');
fs.writeFile('./input.txt',  '我是通过 fs.writeFile() 写入的内容', function(err) {
  if (err) {
    return console.log(err);
  }
  console.log('数据写入成功!');
  console.log('--------分割线---------');
  fs.readFile('./input.txt', function(error, data) {
    console.log(data.toString());
  })
});

// 输出结果
准备写入文件
数据写入成功!
--------分割线---------
我是通过 fs.writeFile() 写入的内容

创建目录

fs.mkdir(path[, options], callback)

option 参数可选

  • recursive 是否以递归的方式创建目录,默认为 false
  • mode 设置目录权限,默认为 0777
fs.mkdir("./test/", function(error) {
  if (error) return console.log(error);
  console.log("目录创建成功!");
});

删除目录

fs.rmdir(path, callback);
fs.rmdir("./test/", function(error) {
  if (error) return console.log(error);
  console.log("目录删除成功!");
});

删除文件

fs.unlink(path, callback);
fs.unlink("./input.txt.gz", function(error) {
  if (error) return console.log(error);
  console.log("文件删除成功!");
});

工具模块

模块名描述
os提供基本的系统操作函数
path提供了处理和转换文件路径的工具
net用于底层的网络通信。提供了服务端和客户端的操作
dns用于解析域名
domain简化异步代码的异常操作,可以捕捉处理 try catch 无法捕捉的
Last Updated:
Contributors: kk