Node.js v12.10.0 文档


repl(交互式解释器)#

中英对照提交修改

稳定性: 2 - 稳定

repl 模块提供了一种“读取-求值-输出”循环(REPL)的实现,它可作为一个独立的程序或嵌入到其他应用中。 可以通过以下方式使用它:

const repl = require('repl');

设计与特性#

中英对照提交修改

repl 模块导出了 repl.REPLServer 类。 当 repl.REPLServer 实例运行时,它接收用户输入的每一行,根据用户定义的解释函数解释这些输入,然后输出结果。 输入可以是 stdin,输出可以是 stdout,或者也可以连接到其他任何 Node.js

repl.REPLServer 实例支持输入的自动补全、精简 Emacs 风格的行编辑、多行输入、ANSI 风格的输出、当前 REPL 会话状态的保存与恢复、错误校正、以及可定制的解释函数。

命令与特殊键#

中英对照提交修改

所有 REPL 的实例都支持下列特殊命令:

  • .break - 在输入一个多行表达式的过程中,输入 .break 命令(或按下 <ctrl>-C 组合键)将终止表达式的继续输入。
  • .clear - 重置 REPL 的 context 为一个空对象,并清除当前正输入的所有多行表达式。
  • .exit - 关闭输入输出流,退出 REPL。
  • .help - 显示特定命令的帮助列表。
  • .save - 保存当前 REPL 会话到一个文件: > .save ./file/to/save.js
  • .load - 读取一个文件到当前 REPL 会话。 > .load ./file/to/load.js
  • .editor 进入编辑模式(<ctrl>-D 完成, <ctrl>-C 取消)
> .editor
// 进入编辑模式(^D 完成,^C 取消)
function welcome(name) {
  return `你好 ${name}!`;
}

welcome('Node.js 用户');

// ^D
'你好 Node.js 用户!'
>

REPL 中下列按键组合有特殊作用:

  • <ctrl>-C - 当按下一次时,与 .break 命令的效果一样。当在空白行按下两次时,与 .exit 命令的效果一样。
  • <ctrl>-D - 与 .exit 命令的效果一样。
  • <tab> - 当在空白行按下时,显示全局和本地作用域内的变量。当在输入时按下,显示相关的自动补全选项。

默认的解释器#

中英对照提交修改

默认情况下,所有 repl.REPLServer 实例使用了一个解释函数,它可以解释 JavaScript 表达式、提供对 Node.js 内置模块的访问。 当 repl.REPLServer 实例被创建时可以传入一个替换的解释函数,覆盖其默认的功能。

JavaScript 表达式#

中英对照提交修改

默认的解释器支持直接解释 JavaScript 表达式:

> 1 + 1
2
> const m = 2
undefined
> m + 1
3

除非在块级作用域中或函数中,否则变量不管是隐式地声明还是使用 constletvar 关键字声明,都是声明在全局作用域中。

全局作用域与局部作用域#

中英对照提交修改

默认的解释器提供了获取存在于全局作用域中的任何变量的途径。 可以通过给每个 REPLServer 绑定的 context 对象指定变量,来显式地把变量暴露给 REPL。 例如:

const repl = require('repl');
const msg = 'message';

repl.start('> ').context.m = msg;

context 对象的属性表现为 REPL 中的局部变量:

$ node repl_test.js
> m
'message'

默认情况下 context 的属性不是只读的。 要指定只读的全局变量, context 的属性必须使用 Object.defineProperty() 来定义:

const repl = require('repl');
const msg = 'message';

const r = repl.start('> ');
Object.defineProperty(r.context, 'm', {
  configurable: false,
  enumerable: true,
  value: msg
});

访问 Node.js 核心模块#

中英对照提交修改

默认的解释器会自动加载被调用的 Node.js 核心模块到 REPL 环境中。 例如,除非被声明为一个全局变量或一个有限范围的变量,否则输入 fs 会被解释为 global.fs = require('fs')

> fs.createReadStream('./some/file');

全局的未捕获异常#

中英对照提交修改

REPL使用 domain 模块来捕获会话期间的所有未捕获异常。

在REPL中使用 domain 模块有如下副作用:

作为独立程序:

process.on('uncaughtException', () => console.log('未捕获的异常'));

throw new Error('foobar');
// 打印:未捕获的异常

当在其他应用程序中使用时:

process.on('uncaughtException', () => console.log('未捕获的异常'));
// 抛出 TypeError [ERR_INVALID_REPL_INPUT]: Listeners for `uncaughtException` cannot be used in the REPL

throw new Error('foobar');
// 抛出:
// Error: foobar

_(下划线)变量的赋值#

中英对照提交修改

默认的解释器会把最近一次解释的表达式的结果赋值给变量 _ (下划线)。 显式地设置 _ 为某个值能禁用该特性。

> [ 'a', 'b', 'c' ]
[ 'a', 'b', 'c' ]
> _.length
3
> _ += 1
Expression assignment to _ now disabled.
4
> 1 + 1
2
> _
4

同样, _error 将指向最后一次看到的错误(如果有的话)。 将 _error 显式设置为值将禁用此行为。

> throw new Error('foo');
Error: foo
> _error.message
'foo'

await 关键词#

中英对照提交修改

使用 --experimental-repl-await 命令行选项,将启用对 await 关键字的实验性支持。

> await Promise.resolve(123)
123
> await Promise.reject(new Error('REPL await'))
Error: REPL await
    at repl:1:45
> const timeout = util.promisify(setTimeout);
undefined
> const old = Date.now(); await timeout(1000); console.log(Date.now() - old);
1002
undefined

自定义的解释函数#

中英对照提交修改

当创建一个新的 repl.REPLServer 时,可以提供一个自定义的解释函数。 这可以用于实现完全定制化的 REPL 应用。

以下是 REPL 的一个假设的示例,执行从一种语言到另一种语言的文本转换:

const repl = require('repl');
const { Translator } = require('translator');

const myTranslator = new Translator('en', 'fr');

function myEval(cmd, context, filename, callback) {
  callback(null, myTranslator.translate(cmd));
}

repl.start({ prompt: '> ', eval: myEval });

可恢复的错误#

中英对照提交修改

当用户正在 REPL 中输入时,按下 <enter> 键会把当前行的输入发送到 eval 函数。 为了支持多行输入, eval 函数可以返回一个 repl.Recoverable 实例给提供的回调函数:

function myEval(cmd, context, filename, callback) {
  let result;
  try {
    result = vm.runInThisContext(cmd);
  } catch (e) {
    if (isRecoverableError(e)) {
      return callback(new repl.Recoverable(e));
    }
  }
  callback(null, result);
}

function isRecoverableError(error) {
  if (error.name === 'SyntaxError') {
    return /^(Unexpected end of input|Unexpected token)/.test(error.message);
  }
  return false;
}

自定义 REPL 输出#

中英对照提交修改

默认情况下,在把输出写入到提供的可写流(默认为 process.stdout)之前,repl.REPLServer 实例会使用 util.inspect() 方法对输出进行格式化。 showProxy 检查选项会默认设置为 true, colors 选项会设置为 true,具体取决于 REPL 的 useColors 选项。

可以在构造时指定 useColors 布尔值选项,以指示默认的编写器使用 ANSI 样式代码来着色来自 util.inspect() 方法的输出。

如果 REPL 作为独立程序运行,则还可以使用 inspect.replDefaults 属性从 REPL 内部更改 REPL 的检查默认值util.inspect(),该属性是 util.inspect() 中的 defaultOptions 的镜像。

> util.inspect.replDefaults.compact = false;
false
> [1]
[
  1
]
>

在构造时,通过在 writer 选项传入一个新的函数,可以完全地自定义一个 repl.REPLServer 实例的输出。 例子,把输入的任何文本转换为大写:

const repl = require('repl');

const r = repl.start({ prompt: '> ', eval: myEval, writer: myWriter });

function myEval(cmd, context, filename, callback) {
  callback(null, cmd);
}

function myWriter(output) {
  return output.toUpperCase();
}

REPLServer 类#

中英对照提交修改

repl.REPLServer 的实例是使用 repl.start() 方法创建的,不能直接地使用 JavaScript 的 new 关键字创建。

'exit' 事件#

中英对照提交修改

当接收到 .exit 命令、或按下两次 <ctrl>-C 发出 SIGINT 信号、或按下 <ctrl>-D 发出 'end' 信号而使 REPL 被退出时,触发 'exit' 事件。 监听器的回调函数被调用时不带任何参数。

replServer.on('exit', () => {
  console.log('从 REPL 接收到 "exit" 事件!');
  process.exit();
});

'reset' 事件#

中英对照提交修改

当 REPL 的上下文被重置时,触发 'reset' 事件。 每当接收到 .clear 命令时会触发该事件,除非 REPL 正在使用默认的解释器并且 repl.REPLServer 实例被创建时 useGlobal 选项被设为 true。 监听器的回调函数被调用时会带上 context 对象作为惟一的参数。

这主要被用于重新初始化 REPL 上下文,使之达到某些预定义的状态,如下面的例子:

const repl = require('repl');

function initializeContext(context) {
  context.m = 'test';
}

const r = repl.start({ prompt: '> ' });
initializeContext(r.context);

r.on('reset', initializeContext);

当代码被执行时,全局的 'm' 变量可以被修改,但随后的 .clear 命令会把它重置回初始值:

$ ./node example.js
> m
'test'
> m = 1
1
> m
1
> .clear
Clearing context...
> m
'test'
>

replServer.defineCommand(keyword, cmd)#

中英对照提交修改

  • keyword <string> 命令关键字(开头不带 . 字符)。
  • cmd <Object> | <Function> 当命令被执行时调用的函数。

replServer.defineCommand() 方法用于添加新的前缀为 . 的命令到 REPL 实例。 这些命令通过输入一个 .keyword 来调用。 cmd 可以是一个函数或一个具有以下属性的对象:

  • help <string> 当键入 .help 时显示的帮助说明(可选)。
  • action <Function> 要执行的函数,可接受一个字符串参数。

例子,添加两个新命令到 REPL 实例:

const repl = require('repl');

const replServer = repl.start({ prompt: '> ' });
replServer.defineCommand('sayhello', {
  help: '打招呼',
  action(name) {
    this.clearBufferedCommand();
    console.log(`你好, ${name}!`);
    this.displayPrompt();
  }
});
replServer.defineCommand('saybye', function saybye() {
  console.log('再见!');
  this.close();
});

在 REPL 实例中使用新的命令:

> .sayhello Node.js中文网
你好,Node.js中文网!
> .saybye
再见!

replServer.displayPrompt([preserveCursor])#

中英对照提交修改

replServer.displayPrompt() 方法会让 REPL 实例做好用户输入的准备,打印配置的 promptoutput 中新的一行,然后返回 input 等待新的输入。

当正在键入多行输入时,会打印省略号而不是提示符。

preserveCursortrue 时,游标位置不会被复位到 0

replServer.displayPrompt 方法主要被使用 replServer.defineCommand() 方法注册的命令的 action 函数调用。

replServer.clearBufferedCommand()#

中英对照提交修改

replServer.clearBufferedCommand() 方法清除已缓冲但尚未执行的任何命令。 此方法主要用于在使用 replServer.defineCommand() 方法注册的命令的 action 函数内调用。

replServer.parseREPLKeyword(keyword[, rest])#

暂无中英对照

  • keyword <string> the potential keyword to parse and execute
  • rest <any> any parameters to the keyword command
  • Returns: <boolean>

稳定性: 0 - 废弃.

An internal method used to parse and execute REPLServer keywords. Returns true if keyword is a valid keyword, otherwise false.

replServer.setupHistory(historyPath, callback)#

中英对照提交修改

初始化 REPL 实例的历史记录日志文件。 当执行 Node.js 二进制文件并使用命令行 REPL 时,默认情况下会初始化历史记录文件。 但是,以编程方式创建 REPL 时不是这种情况。 当以编程方式使用 REPL 实例时,使用此方法初始化历史记录日志文件。

repl.start([options])#

中英对照提交修改

  • options <Object> | <string>

    • prompt <string> 要显示的输入提示符。默认值: '> '(末尾有一个空格)。
    • input <stream.Readable> REPL 输入要被读取的可读流。默认值: process.stdin
    • output <stream.Writable> REPL 输出要被写入的可写流。默认值: process.stdout
    • terminal <boolean> 如果为 true,则指定 output 应被当作一个 TTY 终端。 默认值: 初始化时检查 output 流的 isTTY 属性的值。
    • eval <Function> 当解释每行输入时使用的函数。默认值: JavaScript eval() 函数的异步封装。 eval 函数出错时会返回 repl.Recoverable,表明输入不完整并提示用户完成输入。
    • useColors <boolean> 如果为 true,则指定默认的 writer 函数可以在 REPL 输出中包含 ANSI 颜色风格。 如果提供了自定义的 writer 函数,则该参数无效。 默认值: 如果 REPL 实例的 terminal 值为 true,则检查 output 流上的颜色支持。
    • useGlobal <boolean> 如果为 true,则指定默认的解释函数使用 JavaScript global 作为上下文,而不是为 REPL 实例创建一个新的独立的上下文。    在node命令行(node CLI)交互解释器中,这个值为 true默认值: false
    • ignoreUndefined <boolean> 如果为 true,则指定默认的输出器不会输出命令返回的 undefined 值。 默认值: false
    • writer <Function> 在写入到 output 之前,该函数被调用用来格式化每个命令的输出。 默认值: util.inspect()
    • completer <Function> 可选的函数,用来自定义 Tab 键的自动补全。 详见 readline.InterfaceCompleter
    • replMode <symbol> 一个标志位,指定默认的解释器使用严格模式或默认(sloppy)模式来执行 JavaScript 命令。 可选的值有

      • repl.REPL_MODE_SLOPPY - 使用默认模式解释表达式。
      • repl.REPL_MODE_STRICT - 使用严格模式解释表达式。该模式等同于在每个 repl 声明前加上 'use strict'
    • breakEvalOnSigint - 当接收到 SIGINT 时停止解释当前代码,比如按下 Ctrl+C。 不能与自定义的 eval 函数同时使用。 默认值: false
  • 返回: <repl.REPLServer>

repl.start() 方法创建并启动一个 repl.REPLServer 实例。

如果 options 是一个字符串,则它指定了输入提示符:

const repl = require('repl');

// 一个 Unix 风格的提示符。
repl.start('$ ');

Node.js 的 REPL#

中英对照提交修改

Node.js 自身也使用 repl 模块为执行 JavaScript 代码提供交互接口。 可以通过不带任何参数(或使用 -i 参数)地执行 Node.js 二进制文件来使用它:

$ node
> const a = [1, 2, 3];
undefined
> a
[ 1, 2, 3 ]
> a.forEach((v) => {
...   console.log(v);
...   });
1
2
3

环境变量选项#

中英对照提交修改

使用以下环境变量,可以自定义 Node.js REPL 的各种行为:

  • NODE_REPL_HISTORY - 当给定了一个有效的路径,则 REPL 的历史记录将被保存到指定的文件,而不是用户目录下的 .node_repl_history 文件。 设为 ''(空字符串)将会禁用持久的 REPL 历史记录。 值两头的空格键会被去掉。 在 Windows 平台上,具有空值的环境变量是无效的,因此将此变量设置为一个或多个空格可以禁用持久的 REPL 历史记录。
  • NODE_REPL_HISTORY_SIZE - 控制历史记录的最大行数。必须是正数。默认值: 1000
  • NODE_REPL_MODE - 可以是 'sloppy''strict'。  默认值: 'sloppy',允许代码在非严格模式下运行。

历史记录#

中英对照提交修改

默认情况下,Node.js REPL 模块会把 node REPL 会话之间的历史记录保存到用户目录中的 .node_repl_history 文件。 修改环境变量 NODE_REPL_HISTORY='' 可以禁用该功能。

在高级的行编辑器中使用 Node.js REPL#

中英对照提交修改

对于高级的行编辑器,可以使用环境变量 NODE_NO_READLINE=1 来启动 Node.js。 这会以标准的终端配置来启动主 REPL 和调试 REPL,可以使用 rlwrap

例如,可以在 .bashrc 文件中添加:

alias node="env NODE_NO_READLINE=1 rlwrap node"

在一个 Node.js 实例中启动多个 REPL 实例#

中英对照提交修改

可以在一个 Node.js 实例中创建并运行多个 REPL 实例,它们共享一个 global 对象但有独立的 I/O 接口。

例子,在 stdin、Unix socket、和 TCP socket 上分别提供了独立的 REPL:

const net = require('net');
const repl = require('repl');
let connections = 0;

repl.start({
  prompt: 'Node.js 使用 stdin> ',
  input: process.stdin,
  output: process.stdout
});

net.createServer((socket) => {
  connections += 1;
  repl.start({
    prompt: 'Node.js 使用 Unix socket> ',
    input: socket,
    output: socket
  }).on('exit', () => {
    socket.end();
  });
}).listen('/tmp/node-repl-sock');

net.createServer((socket) => {
  connections += 1;
  repl.start({
    prompt: 'Node.js 使用 TCP socket> ',
    input: socket,
    output: socket
  }).on('exit', () => {
    socket.end();
  });
}).listen(5001);

从命令行运行这个应用会在 stdin 上启动一个 REPL。 其他 REPL 客户端可以通过 Unix socket 或 TCP socket 进行连接。 例如,可以使用 telnet 连接到 TCP socket,使用 socat 连接到 Unix socket 或 TCP socket。

通过从一个基于 Unix socket 的服务器(而不是 stdin)启动一个 REPL,可以连接到一个长期运行的 Node.js 进程而无需重启它。

例子,在一个 net.Server 实例和一个 net.Socket 实例上运行一个全特性的(terminal)REPL,详见:https://gist.github.com/TooTallNate/2209310

例子,在 curl(1) 上运行一个 REPL 实例,详见:https://gist.github.com/TooTallNate/2053342