开发手册 欢迎您!
软件开发者资料库

交互式解释器(REPL) | Node.js

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。Node.js 的包管理器 npm,是全球最大的开源库生态系统。

Node.js v8.x 中文文档


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

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

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

默认的解释器#

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

JavaScript 表达式#

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

> 1 + 12> const m = 2undefined> m + 13

除非在块级作用域中或函数中,否则变量不管是隐式地声明还是使用 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');

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

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

> [ 'a', 'b', 'c' ][ 'a', 'b', 'c' ]> _.length3> _ += 1Expression assignment to _ now disabled.4> 1 + 12> _4

自定义的解释函数#

当创建一个新的 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 中输入时,按下 键会把当前行的输入发送到 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() 方法对输出进行格式化。使用 util.inspect() 方法时,useColors 选项可被指定是否在建立默认输出器时使用 ANSI 风格的代码给输出上色。

在构造时,通过在 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 类继承自 readline.Interface 类。repl.REPLServer 的实例由 repl.start() 方法创建,不能直接使用 JavaScript 的 new 关键字创建。

'exit' 事件#

当接收到 .exit 命令、或按下两次 -C 发出 SIGINT 信号、或按下 -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 = 11> m1> .clearClearing context...> m'test'>

replServer.defineCommand(keyword, cmd)#