Skip to content

前言 ​

Node.js 是一个开源的、跨平台的 JavaScript 运行时环境。 Node.js 并不是语言,而是一个 JavaScript 运行时环境,它的语言是 JavaScript。这就跟 PHP、Python、Ruby 这类不一样,它们既代表语言,也可代表执行它们的运行时环境(或解释器)。

Node.js 与 JavaScript 真要按分层说,如下图:

CommonJS源码解析 ​

Node.js 一直以来都是以 CommonJS 作为其模块的基座,而在最新几个大版本中则加入了对 ESM 的支持。

CommonJS 是运行时做的模块加载和运行,它可以在代码执行一半的时候以动态的方式加载,这种方法在一些静态分析的时候会造成阻碍。而 ESM 则是在模块顶部以语法的形式加载模块,完全可以做静态分析。

必选项 ​

  • 第一个,require。require 是一个函数,这个函数有一个参数代表模块标识,它的返回值就是其所引用的外部模块所暴露的 API。
  • 第二个,模块上下文。在一个 CommonJS 的模块上下文中,需要有满足如下条件的一些事项存在:
    • require 函数
    • exports 对象,这是一个用于导出模块内容的通道
    • module 对象,内含该模块元信息,比如一个制度的 id 字段;实际上,Node.js 中的 module 下还含了初始的 exports 对象。
  • 第三个,模块标识。模块标识其实就是一个字符串,用于传给 require 函数。

模块包装函数 ​

  • Node.js 会将 xxx.js 中的代码包裹在一个函数中执行。
  • 这个函数的形式是 function (exports, require, module, __filename, __dirname) { ... }。
  • arguments.callee 是一个指向当前正在执行的函数的引用。
js
// example.js
console.log('' + arguments.callee); 

//bash
node example.js

//console.log
function (exports, require, module, __filename, __dirname) {
    console.log('' + arguments.callee);
}

易错点整理 ​

  1. 不要赋值给 exports,导出方式只有默认导出和具名导出。
js
module.exports = {}  //默认导出:使用 module.exports
exports.a = 1  //具名导出:直接在 exports 对象上添加属性

//错误示例
exports = {} // exports 是一个对象的引用,直接赋值给 exports 会导致它与 module.exports 断开连接。
  1. 同时使用exports和module.exports
  • 问题出在module.exports = exports = {};,exports是module.exports对象的引用,如果把module.exports指向新的对象,那么exports就会丢失。
  • 解决方案:不要同时使用两种导出方式
js
module.exports = { name, age };
exports.printName = printName;

const { name, age, printName } = require('./user.js');// name age undefined
  1. 多次导入模块会被缓存
  • Node.js 使用模块缓存机制,确保同一个模块在同一个进程中只会被加载和执行一次。
  • 当一个模块被第一次导入时,Node.js 会执行该模块的代码,并将导出的对象缓存起来。
  • 之后再次导入同一个模块时,Node.js 会直接从缓存中获取导出的对象,而不会再次执行模块的代码。
  • 解决方案:
    • 导出一个函数,让函数返回一个对象,这样每次导入的都是同一个函数,函数内部返回一个对象,对象中包含需要导出的属性。
    • 删除模块缓存:delete require.cache[require.resolve('./module')]
      • require.cache: 模块被加载时缓存在此对象中。
      • require.resolve: 定位到模块位置,返回模块的解析后的文件名。
  1. CJS 下加载 ESM 模块 ECMAScript module 可以通过 import 加载 CommonJS 模块,而反过来 CommonJS 模块是无法通过 require() 来加载 ECMAScript module 的。这里涉及到一个本质问题,那就是模块加载的异同步。CommonJS 的 require() 机制是完全同步的,而 ECMAScript module 的 import 机制则是异步的。

import 是异步的,那么在内部通过同步的方式模拟一个 require 流程是没问题的,所以 ECMAScript module 下可以通过 import 去加载 CommonJS 模块;反过来不行,一个同步的东西是无法加载异步内容的,至少无法通过比较正统的方式解决。

Node.js 的 CommonJS 模块虽然不支持 import 语法,但它却支持 import() 语法动态载入(dynamic import)。我们仍可以在 CommonJS 中通过 import() 函数来加载一个 ECMAScript module。

虽然在 import * as mod from 'xxx' 的语法中我们看起来是同步的,但其实在引擎内部帮你吃掉了异步的部分。然而在 import() 中,该异步需要自己处理,它的返回值是一个 Promise。如:

js
    import('xxx').then(mod => {
      // 这里的 mod 就是加载的 ECMAScript module
    });

Node.js核心模块 ​

Modules:CommonJS模块 ​

特性

  1. require:加载文件模块导出的对象。
  2. module.exports:存储文件模块导出的对象。
  3. __dirname: 当前模块的文件夹路径。(以形参为参数传入)
  4. __filename: 当前模块的文件路径。(以形参为参数传入)
  5. exports:模块导出对象,是module.exports的别名。
  6. require.cache:存储已加载的模块的缓存对象。
  7. require.main:对应的是入口模块的 Module 实例。
  8. require.resolve:返回模块解析后的文件路径。
js
console.log(JSON.stringify(require.main, null, 2)); //查看主入口module对象

Modules:ECMAScript模块 ​

ESM是打包JavaScript代码以供重复使用的官方标准格式。模块是使用import和export相关的语句定义的。

生效规则

  1. 如果文件扩展名为.js。只有最近的package.json文件的type属性值为module时,Node.js才会认为文件使用的是ESM规范的模块,其它情况都会认为采用的是CommonJS规范的模块。
  2. 无论type字段的值如何,.mjs文件始终被视为ES模块,.cjs文件总是被视为CommonJS模块。

特性

  1. 导入断言:目前只支持json类型。
js
// animal.json
{
    "name": "monkey"
}

// index.mjs
import animal from './animal.json' assert { type: 'json' };
console.log('animal', animal);
  1. 直接在内置的模块中导出API
js
import { readFile } from 'node:fs';
readFile(new URL('./foo.txt', import.meta.url), { encoding: 'utf8' }, (err, source) => {
  if (err) {
    console.error(err);
  } else {
    console.log(source);
  }
});
  1. import.meta 包含url和resolve属性的对象。
  • import.meta.url:返回当前文件的url路径,以file://协议开头。
  • import.meta.resolve:返回需要被解析的文件的url路径,以file://协议开头。
js
// file:///app/index.mjs 
console.log(import.meta.url);
// file:///app/user.mjs 
console.log(import.meta.resolve('./user.mjs'));
  1. 与CommonJS的互相操作 import语可以引入ES模块或者CommonJS模块,import语句只能在ES模块中使用。导入CommonJS模块时,module.exports对象作为default导出。
js
module.exports = { height, age, name }

import user from './user.js'; //{ height: 133, age: 23, name: 'liuhuakawaii' }
import { name } from './user.js'; //liuhuakawaii
import { default as user3 } from './user.js'; //{ height: 133, age: 23, name: 'liuhuakawaii' }

//{ default: { height: 133, age: 23, name: 'liuhuakawaii' }, height: 133, age: 23, name: 'liuhuakawaii' }
import * as user2 from './user.js';

总结:ESM和CommonJS的区别

  1. 语法: ESM 使用 import 和 export,CommonJS 使用 require 和 module.exports。

  2. 模块加载机制

    • ESM 使用静态加载机制,模块的依赖关系在编译时确定。ESM 支持异步加载模块,使用 import() 函数可以动态加载模块。
    • CommonJS 使用动态加载机制,模块的依赖关系在运行时确定。CommonJS 模块是同步加载的,require 函数会阻塞执行,直到模块加载完成。
  3. ESM 使用相对路径导入模块,CommonJS 可以使用相对路径或绝对路径导入模块。

  4. 模块缓存

    • ESM 也有模块缓存机制,但与 CommonJS 的缓存机制不同。ESM 的缓存是基于模块的 URL 而不是模块的标识符。
    • CommonJS 使用模块缓存机制,确保每个模块在同一个进程中只会被加载和执行一次。缓存是基于模块的标识符。
  5. 默认导出

    • ESM 支持默认导出,使用 export default 语句。使用 import 语句时,可以为默认导出指定任意名称。
    • CommonJS 没有专门的默认导出语法,通常将整个模块导出为一个对象。
  6. 循环依赖处理

    • ESM 处理循环依赖的方式与 CommonJS 不同。ESM 在模块加载时会保留模块的引用,即使模块尚未完全执行。
    • CommonJS 处理循环依赖的方式是,当模块 A 依赖模块 B,而模块 B 又依赖模块 A 时,模块 A 的导出对象会被缓存,即使模块 A 尚未完全执行。
  7. **浏览器支持:**ESM 是浏览器原生支持的,CommonJS 需要打包工具转换。

Modules:node:module API ​

  1. module.builtinModules Node.js提供的所有模块的名称列表。可用于验证模块是否由第三方维护。
js
const builtin = require('node:module').builtinModules; //返回所有模块名称
  1. module.createRequire(filename) 创建require函数。filename参数必须是URL对象、URL字符串,或者是绝对路径。

  2. module.isBuiltin(moduleName) 检测模块是否是内置模块。

js
const isBuiltin = require('node:module').isBuiltin;
console.log(isBuiltin('node:path')); // true
console.log(isBuiltin('path')); // true
console.log(isBuiltin('wss')); // false

Buffer模块 ​

Buffer对象表示固定长度的字节序列。 虽然Buffer类在全局范围内可用,但仍然建议通过import或require语句显式引用它。

API

  1. Buffer.alloc(size, fill, encoding) :创建buffer对象。默认填充为0。
  2. Buffer.from(string | buffer):根据已有的buffer或者字符串创建Buffer对象。
  3. Buffer.byteLength(string, encoding):返回字符串编码后的字节长度。
  4. Buffer.concat(list, totalLength):返回结合后最新的buffer对象。
  5. Buffer.isBuffer(obj):如果是buffer对象,则返回true。
  6. buf.fill(value, offset, end, encoding)
  7. buf.includes(value):如果value在buf中被找到,则返回true。
  8. buf.indexOf(value):查找value在buffer中的索引,没有找到则返回-1。
  9. buf.subarray(start, end):根据start和end截取出一个新的buffer对象,原对象不会受影响。

URL模块 ​

URL模块提供了一些非常实用的用于解析URL的方法。

  1. URL对象
js
const myURL = new URL('https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash');
console.log(myURL);
/*
{
  href: 'https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash',
  origin: 'https://sub.example.com:8080',
  protocol: 'https:',
  username: 'user',
  password: 'pass',
  host: 'sub.example.com:8080',
  hostname: 'sub.example.com',
  port: '8080',
  pathname: '/p/a/t/h',
  search: '?query=string',
  searchParams: URLSearchParams { 'query' => 'string' },
  hash: '#hash'
}
*/

修改URL信息

js
const url = require('node:url');
const myURL = new URL('https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash'); 
myURL.port = 8081;
// https://user:pass@sub.example.com:8081/p/a/t/h?query=string#hash
console.log(myURL.toString());
  1. URLSearchParams对象 用于读取和写入URL的query信息。
js
const myURL = new URL('https://example.org/?abc=123');

//查询search
myURL.searchParams.get('abc'); //123
myURL.searchParams.has('abc'); //true

//添加参数
myURL.searchParams.append('abc', '456');

//删除参数
myURL.searchParams.delete('abc');

//修改参数
myURL.searchParams.set('abc', '789');

手动创建URLSearchParams对象

js
const newSearchParams = new URLSearchParams('a=b');
newSearchParams.append('a', 'c');

console.log(newSearchParams.toString());// a=b&a=c

Query String模块 ​

Query String模块提供了一些非常实用的用于解析查询字符串的方法。

  1. querystring.parse(str, sep, eq): 解析查询字符串。
js
const querystring = require('node:querystring'); 

const obj = querystring.parse('a=b&c=d&c=f');
console.log(obj);  // { a: 'b', c: [ 'd', 'f' ] }
  1. querystring.stringify(obj, sep, eq): 将对象序列化成查询字符串。
js
const obj = {
    a: 'b',
    c: ['d', 'f']
};

console.log(querystring.stringify(obj)); // a=b&c=d&c=f
console.log(querystring.stringify(obj, ';', ':')); // a:b;c:d;c:f

path模块 ​

path模块提供了用于处理文件和目录路径的实用程序。

  1. path.basename(path, suffix): 返回路径的最后一部分。
js
const path = require('node:path'); 

path.basename('/foo/bar/baz/asdf/index.html'); // index.html
path.basename('/foo/bar/baz/asdf/index.html', '.html');// index
  1. path.delimiter: 返回特定平台的路径之间分隔符。Windows使用的是;,POSIX使用的是:。
js
import path from 'path';

console.log(path.delimiter);
console.log(process.env.PATH); //可执行文件的路径集合
  1. path.dirname(path): 返回路径的目录名。
js
path.dirname('/foo/bar/baz/asdf/quux'); // /foo/bar/baz/asdf
  1. path.extname(path): 返回路径的扩展名。根据最后一个.字符到路径结尾的字符串。
js
path.extname('index.html'); // .html
path.extname('index.'); // .
path.extname('index'); // ''
  1. path.isAbsoute(path): 如果path是绝对路径则返回true。
js
path.isAbsolute('/foo/bar'); // true
path.isAbsolute('/baz/..'); // true
path.isAbsolute('qux/');    // false
path.isAbsolute('.');       // false
  1. path.join([...paths]): 将所有给定的路径片段连接在一起,然后对生成的路径进行规范化。
js
path.join('/foo', 'bar', 'baz/asdf', 'quux', '..'); // /foo/bar/baz/asdf
path.join('./foo', 'bar/', '../', './baz'); //  ./foo/baz
  1. path.normalize(path): 规范化给定的路径,解析..和.片段。
js
path.normalize('/foo/bar//baz/asdf/quux/..'); // /foo/bar/baz/asdf
  1. path.relative(from, to):根据当前工作目录返回从from到to的相对路径。
js
path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb'); // ../../impl/bbb
  1. path.resolve([...paths]):将一系列的路径片段解析为绝对路径。
js
path.resolve('/foo/bar', './baz'); // /foo/bar/baz
path.resolve('/foo/bar', '/tmp/file/'); // /tmp/file
  1. path.sep:返回特定平台的路径中目录之间分隔符。Windows使用的是\,POSIX使用的是/。

OS模块 ​

OS模块提供了和操作系统相关的一些属性和方法。

os.EOL 操作系统特定的行结束标记。Windows使用的是\n,POSIX使用的是\r\n。

File System模块 ​

node:fs模块与文件系统进行交互。

  1. 基本使用
js
const fs = require('node:fs/promises');// 使用基于Promise的API
const fs = require('node:fs');// 使用基于回调函数或者同步操作的API
  1. 创建文件夹
js
await fs.mkdir('test');
  1. 写入文件(如果文件不存在,则创建文件)
js
await fs.writeFile('test.txt', 'hello world');
  1. 追加文件内容
js
await fs.appendFile('test.txt', 'hello world');
  1. 读取文件夹
js
const files = await fs.readdir('.');
  1. 读取文件
js
const data = await fs.readFile('test.txt');
  1. 区分文件夹还是文件、判断文件或者文件夹是否存在
js
const stat = await fs.stat('test.txt');
 if (stat.isDirectory()) {
        console.log('这是一个文件夹');
  } else if (stat.isFile()) {
        console.log('这是一个文件');
  }
  1. 重命名文件
js
await fs.rename('test.txt', 'test2.txt');
  1. 删除文件夹
js
await fs.rmdir('test');
  1. 删除文件
js
await fs.unlink('test2.txt');
  1. 实战:递归创建文件夹
js
const { mkdirSync, statSync } = require('node:fs');
const path = require('node:path');

function mkdirs(dir) {
    const normalizedDir = path.normalize(dir);
    const dirs = normalizedDir.split(path.sep);
    dirs.forEach((currentDir, index) => {
        const dirPath = dirs.slice(0, index + 1).join(path.sep);
        if (currentDir) {
            let isExisted = false;
            try {
                isExisted = statSync(dirPath).isDirectory();
            } catch (err) {
               // 说明文件夹不存在
            }

            if (!isExisted) {
                mkdirSync(dirPath);
            }
        }
    });
}

mkdirs(path.resolve(__dirname, './temp/logs'));

process模块 ​

process对象提供有关当前Node.js进程的信息。

  1. process.argv 和 process.argv0process.argv属性返回一个数组,该数组包含Node.js进程启动时传递的命令行参数。 第一个元素将是process.execPath。如果需要访问argv[0]的原始值,请使用process.argv0。第二个元素将是正在执行的JavaScript文件的路径。剩下的元素将是任何额外的命令行参数。

  2. process.cwd() 返回Node.js进程的当前工作目录。

  3. process.chdir(directory) 更改Node.js进程的当前工作目录。如果目录不存在,则抛出异常。

  4. process.env 返回一个包含用户环境的对象。可以手动添加一些环境信息到对象里面。

js
console.log(process.env);
process.env.mode = 'development'; // 添加环境变量
console.log(process.env);

Events模块 ​

Node.js的大部分核心API都是围绕着一种异步事件驱动架构构建的。在这种架构中,某些类型的对象(称为“emittters”)会发出特定的具名事件,因此导致调用Function对象(“listeners”)。

  1. 基本用法
js
import { EventEmitter } from 'node:events';

class MyEmitter extends EventEmitter { }
const myEmitter = new MyEmitter();

myEmitter.on('event', () => {
  console.log('an event occurred!');
});

myEmitter.emit('event');
  1. 传递参数和listener中的this
js
myEmitter.on('event', function (a, b) {
  console.log(a, b, this === myEmitter);  // a, b, true
});

myEmitter.emit('event', 'a', 'b');
  1. 只触发一次的事件函数 eventEmitter.on方法可以触发多次,而eventEmitter.once方法可以只触发一次。
js
let m = 0;
myEmitter.once('event', () => {
  console.log(++m);
});

myEmitter.emit('event');// 1
myEmitter.emit('event'); // 没有反应
  1. on可以绑定多个listeners,eventEmitter.listeners返回函数列表。

  2. 返回所有的事件名称,eventEmitter.eventNames返回所有的事件名称列表。

js
const myEE = new EventEmitter();
myEE.on('foo', () => {});
myEE.on('bar', () => {});

console.log(myEE.eventNames());// ['foo', 'bar']
  1. 移除listener:eventEmitter.off是eventEmitter.removeListener 的别名。

  2. 移除所有的listener: eventEmitter.removeAllListeners 移除指定的事件所有绑定过的回调函数。

Stream模块 ​

Stream是一个抽象接口,用于处理Node.js中的流数据。node:stream模块为实现流接口提供了API。 Stream可以是可读的,也可以是可写的,或者两者都有。所有流都是EventEmitter的实例。 Node.js提供了很多流对象,比如操作文件系统、对HTTP服务的请求等。 通常在处理大体积文件的时候用到流,防止占用过多的内存。

  1. 创建文件只读流
js
// 创建只读流
const { createReadStream, statSync } = require('node:fs');

// highWaterMark
const stream = createReadStream('./demo.txt', { encoding: 'utf8' });

let totalLength = 0;
stream.on('data', (chunk) => {
    totalLength += Buffer.byteLength(chunk);
});

stream.on('end', () => {
    console.log(`数据读取完毕`);
    console.log(statSync('./demo.txt').size);
    console.log(totalLength);
});
  1. 创建指定范围的只读流
js
const rangeStream = createReadStream('./demo.txt', { encoding: 'utf8', start: 3, end: 5 });

let content = '';
rangeStream.on('data', (chunk) => {
    content += chunk;
});

rangeStream.on('end', () => {
    console.log(content); // 一个字符
});
  1. 创建文件可写流
js
// 复制文件
const { createReadStream, createWriteStream, statSync } = require('node:fs');

const readStream = createReadStream('./demo.txt', { encoding: 'utf8' });
const writeStream = createWriteStream('./demo-write.txt', { encoding: 'utf8' });

// 方式1:手动写入
readStream.on('data', (chunk) => {
    writeStream.write(chunk);
});

readStream.on('end', () => {
    console.log(`数据写入完毕`);
    console.log(statSync('./demo.txt').size === statSync('./demo-write.txt').size);
});

// 方式2:通过管道完成写入
readStream
    .on('end', () => {
        console.log(`数据读取完毕`);
    })
    .pipe(writeStream)
    .on('finish', () => {
        console.log(`写入完毕`);
        console.log(statSync('./demo.txt').size === statSync('./demo-write.txt').size);
    })
  1. 转换流
js
// 压缩文件
const { createReadStream, createWriteStream } = require('node:fs');
const zlib = require('node:zlib');

const readStream = createReadStream('./demo.txt');
const duplexStream = zlib.createGzip();
const writeStream = createWriteStream('./demo.tar.gz');

readStream.pipe(duplexStream).pipe(writeStream);
  1. 下载文件
js
const { createReadStream } = require('node:fs');
const zlib = require('node:zlib');
const http = require('node:http');

http.createServer((req, res) => {
    res.writeHead(200, {
        'Content-Type': 'application/octet-stream',
        'Content-Encoding': 'gzip',
        'Content-Disposition': 'attachment; filename=demo.tar.gz',
    });
    const readStream = createReadStream('./demo.txt');
    const duplexStream = zlib.createGzip();

    readStream.pipe(duplexStream).pipe(res);
}).listen(8890);

readline模块 ​

node:readline模块提供了一个接口,用于一次一行地从Readable流读取数据。

  1. 问题提示
js
const readline = require('node:readline');
const { stdin: input, stdout: output } = require('node:process');

const rl = readline.createInterface({ input, output });

rl.question('What do you think of Node.js? ', (answer) => {
    console.log(answer);
    rl.close();
});
  1. 逐行读取文件内容
js

const { createInterface } = require('node:readline');
const { once } = require('node:events');
const { createReadStream } = require('node:fs');

(async () => {
    const rl = createInterface({
        input: createReadStream('./demo.txt'),
    });
    
    let line = 0;
    rl.on('line', (lineContent) => {
        console.log(`${++line}:`, lineContent.toString());
    });
    
    await once(rl, 'close');
    
    console.log('File read complete.');
})();
  1. 轻量的CLI
js
const readline = require('node:readline');
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  prompt: 'HAHA> ',
});

rl.prompt();

rl.on('line', (line) => {
  switch (line.trim()) {
    case 'hello':
      console.log('world!');
      break;
    default:
      console.log(`Say what? I might have heard '${line.trim()}'`);
      break;
  }
  rl.prompt();
}).on('close', () => {
  console.log('Have a great day!');
  process.exit(0);
});

http模块 ​

http模块是一个非常常用的模块,我们可以用它来搭建一个服务器,或者使用它发送请求。

js
    const http = require('http')
    const url = require('url')
    const qs = require('querystring')
    const server = http.createServer((req, res) => {

      //http://localhost:3000/getinfo
      const { pathname, query } = url.parse(req.url)

      if (req.method === 'GET' && pathname === '/getinfo') {
        console.log(query)
        console.log(qs.parse(query)) //格式化成js对象
        res.setHeader('Content-Type', 'text/plain;charset=utf-8') //中文乱码处理
        res.end('响应了getinfo API请求')
      } else if (req.method === 'POST' && pathname === '/postinfo') {
        let data = '' //存储结果  
        req.on('data', temp => {//post数据返回可能是一段一段的
          data += temp
        })

        req.on('end', () => {//接受完毕
          console.log(qs.parse(data));
        })

        res.setHeader('Content-Type', 'text/plain;charset=utf-8') //中文乱码处理
        res.end('响应了POST请求')
      } else {
        res.statusCode = 404
        res.end('Not Found')
      }
    })

    server.listen(3000, () => {
      console.log('Server is running');
    })

dns模块 ​

Node.js 的 dns 模块是一个内置模块,用于处理与域名服务器(DNS)相关的操作。这个模块提供了一系列的函数,可以用来解析域名,查找域名对应的 IP 地址,以及其他与 DNS 查询相关的操作。

以下是 dns 模块的一些主要函数:

  1. dns.lookup(hostname[, options], callback):这个函数将一个域名(如 'google.com')解析为第一条找到的 IPv4 或 IPv6 地址。它使用底层操作系统工具进行域名解析,可以使用 options 参数来指定 IP 地址的版本和其他选项。

  2. dns.resolve4(hostname, callback):这个函数将一个域名解析为一个 IPv4 地址数组。它使用网络进行 DNS 解析,不受本地主机文件或者其他可能影响 dns.lookup 的因素的影响。

  3. dns.resolve6(hostname, callback):这个函数将一个域名解析为一个 IPv6 地址数组。它的工作方式和 dns.resolve4 类似,只是返回的是 IPv6 地址。

  4. dns.resolveMx(hostname, callback):这个函数用于解析一个域名的邮件交换(MX)记录。

  5. dns.reverse(ip, callback):这个函数用于进行反向 DNS 查询,将一个 IP 地址解析为一个域名。

这些函数都是异步的,它们的结果会通过回调函数返回。回调函数的第一个参数是一个可能存在的错误对象,如果没有错误,那么这个参数就是 null。其余的参数是查询的结果。

js
const dns = require('dns');
dns.lookup('google.com', (err, address) => {
  if (err) throw err;
  console.log(address);
});