- assert - 断言
- async_hooks - 异步钩子
- Buffer - 缓冲器
- child_process - 子进程
- cluster - 集群
- console - 控制台
- crypto - 加密
- debugger - 调试器
- dgram - 数据报
- dns - 域名服务器
- domain - 域
- Error - 错误
- events - 事件触发器
- fs - 文件系统
- global - 全局变量
- http - HTTP
- http2 - HTTP/2
- https - HTTPS
- inspector - 检查器
- module - 模块
- net - 网络
- os - 操作系统
- path - 路径
- perf_hooks - 性能钩子
- process - 进程
- punycode - 域名代码
- querystring - 查询字符串
- readline - 逐行读取
- repl - 交互式解释器
- stream - 流
- string_decoder - 字符串解码器
- timer - 定时器
- tls - 安全传输层
- trace_events - 跟踪事件
- tty - 终端
- url - URL
- util - 实用工具
- v8 - V8引擎
- vm - 虚拟机
- wasi - WASI
- worker_threads - 工作线程
- zlib - 压缩
repl(交互式解释器)#
源代码: lib/repl.js
repl
模块提供了一种“读取-求值-输出”循环(REPL)的实现,它可作为一个独立的程序或嵌入到其他应用中。
可以通过以下方式使用它:
const repl = require('repl');
设计与特性#
repl
模块导出了 repl.REPLServer
类。
当 repl.REPLServer
实例运行时,它接收用户输入的每一行,根据用户定义的解释函数解释这些输入,然后输出结果。
输入可以是 stdin
,输出可以是 stdout
,或者也可以连接到其他任何 Node.js 流。
repl.REPLServer
实例支持输入的自动补全、完成的预览、精简 Emacs 风格的行编辑、多行输入、类似 ZSH 的反向i搜索、类似 ZSH 的基于子字符串的历史搜索、ANSI 风格的输出、当前 REPL 会话状态的保存与恢复、错误校正、以及可定制的解释函数。
不支持 ANSI 风格和 Emacs 风格的行编辑的终端会自动地回退到有限的特性集。
命令与特殊键#
所有 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: 当在空白行按下时,显示全局和本地作用域内的变量。当在输入时按下,显示相关的自动补全选项。
有关与反向i搜索相关的快捷键,请参见反向i搜索。 有关所有的其他快捷键,请参见 TTY 快捷键。
默认的解释器#
默认情况下,所有 repl.REPLServer
实例使用了一个解释函数,它可以解释 JavaScript 表达式、提供对 Node.js 内置模块的访问。
当 repl.REPLServer
实例被创建时可以传入一个替换的解释函数,覆盖其默认的功能。
JavaScript 表达式#
默认的解释器支持直接解释 JavaScript 表达式:
> 1 + 1
2
> const m = 2
undefined
> m + 1
3
除非在块级作用域中或函数中,否则变量不管是隐式地声明还是使用 const
、 let
或 var
关键字声明,都是声明在全局作用域中。
全局作用域与局部作用域#
默认的解释器提供了获取存在于全局作用域中的任何变量的途径。
可以通过给每个 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 会话的所有未捕获的异常。
在 REPL 中对 domain
模块的这种使用具有以下的副作用:
- 未捕获的异常仅在独立的 REPL 中触发
'uncaughtException'
事件。 在另一个 Node.js 程序的 REPL 中添加此事件的监听器会抛出ERR_INVALID_REPL_INPUT
。 - 尝试使用
process.setUncaughtExceptionCaptureCallback()
会抛出ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE
错误。
作为独立程序:
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
反向i搜索#
The REPL supports bi-directional reverse-i-search similar to ZSH. It is triggered with Ctrl+R to search backward and Ctrl+S to search forwards.
Duplicated history entires will be skipped.
Entries are accepted as soon as any button is pressed that doesn't correspond with the reverse search. Cancelling is possible by pressing Esc or Ctrl+C.
Changing the direction immediately searches for the next entry in the expected direction from the current position on.
自定义的解释函数#
当创建一个新的 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 类#
options
<Object> | <string> 参见repl.start()
。- 继承自: <readline.Interface>
repl.REPLServer
的实例是使用 repl.start()
方法或直接使用 JavaScript 的 new
关键字创建。
const repl = require('repl');
const options = { useColors: true };
const firstInstance = repl.start(options);
const secondInstance = new repl.REPLServer(options);
'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])
#
preserveCursor
<boolean>
replServer.displayPrompt()
方法会让 REPL 实例做好用户输入的准备,打印配置的 prompt
到 output
中新的一行,然后返回 input
等待新的输入。
当正在键入多行输入时,会打印省略号而不是提示符。
当 preserveCursor
为 true
时,游标位置不会被复位到 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 executerest
<any> any parameters to the keyword command- Returns: <boolean>
An internal method used to parse and execute REPLServer
keywords.
Returns true
if keyword
is a valid keyword, otherwise false
.
replServer.setupHistory(historyPath, callback)
#
historyPath
<string> 历史文件的路径。callback
<Function> 当历史记录写入已准备好或出错时调用。err
<Error>repl
<repl.REPLServer>
初始化 REPL 实例的历史记录日志文件。 当执行 Node.js 二进制文件并使用命令行 REPL 时,默认情况下会初始化历史记录文件。 但是,以编程方式创建 REPL 时不是这种情况。 当以编程方式使用 REPL 实例时,使用此方法初始化历史记录日志文件。
repl.builtinModules
#
A list of the names of all Node.js modules, e.g., 'http'
.
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> 当解释每行输入时使用的函数。默认值: JavaScripteval()
函数的异步封装。eval
函数出错时会返回repl.Recoverable
,表明输入不完整并提示用户完成输入。useColors
<boolean> 如果为true
,则指定默认的writer
函数可以在 REPL 输出中包含 ANSI 颜色风格。 如果提供了自定义的writer
函数,则该参数无效。 默认值: 如果 REPL 实例的terminal
值为true
,则检查output
流上的颜色支持。useGlobal
<boolean> 如果为true
,则指定默认的解释函数使用 JavaScriptglobal
作为上下文,而不是为 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
<boolean> 当接收到SIGINT
时停止解释当前代码,比如当按下 Ctrl+C。 不能与自定义的eval
函数同时使用。 默认值:false
。preview
<boolean> 定义 repl 是否打印自动补全并输出预览。 默认值: 如果使用默认的 eval 函数,则为true
,如果使用自定义的 eval 函数,则为false
。 如果terminal
为假,则没有预览,并且preview
的值无效。
- 返回: <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。