开发者社区> 异步社区> 正文

《Node学习指南》一2.3 多行以及更复杂的JavaScript

简介:
+关注继续查看

本节书摘来自异步社区《Node学习指南》一书中的第2章,第2.3节,作者【美】Shelley Powers,更多章节内容可以访问云栖社区“异步社区”公众号查看

2.3 多行以及更复杂的JavaScript

Node学习指南
你可以像写文件一样在REPL中输入JavaScript,包括导入module的require语句。以下代码显示了如何使用Query String(qs)module:

$ node
> qs = require('querystring');
{ unescapeBuffer: [Function],
 unescape: [Function],
 escape: [Function],
 encode: [Function],
 stringify: [Function],
 decode: [Function],
 parse: [Function] }
> val = qs.parse('file=main&file=secondary&test=one').file;
[ 'main', 'secondary' ]

由于没有使用var关键字,表达式的结果被直接输出,在本例中是querystring对象的接口。预期之外的收获是用这种方式不仅可以访问对象,同时还可以了解更多关于对象的可用接口。但是,如果不想看到可能出现的长文本输出,请使用var关键字:

> var qs = require('querystring');

可以用qs变量访问querystring对象的任一方法。

为了兼容外部模块,REPL可以处理多行表达式,提供了可以嵌套使用的文本标识符,跟在大括号{}之后:

> var test = function (x, y) {
... var val = x * y;
... return val;
... };
undefined
> test(3,4);
12

REPL提供重复的点“.”符号跟在开放的大括号后面表示输入命令未完成,该符号同样可以用于不闭合的小括号:

> test(4,
... 5);
20

层级间的递进需要更多的点符号。这在交互式环境中是必须的,否则会在输入过程中迷失了自己当前所在的位置:

> var test = function (x, y) {
... var test2 = function (x, y) {
..... return x * y;
..... }
... return test2(x,y);
 ...}
undefined
> test(3,4);
12
>

以下代码是一个完整的Node应用程序,可以在REPL中输入或者复制粘贴并运行:

> var http = require('http');
undefined
> http.createServer(function (req, res) {
...
...   // content header
...   res.writeHead(200, {'Content-Type': 'text/plain'});
...
...  res.end("Hello person\n");
... }).listen(8124);
{ connections: 0,
  allowHalfOpen: true,
  _handle:
  { writeQueueSize: 0,
   onconnection: [Function: onconnection],
   socket: [Circular] },
  _events:
  { request: [Function],
   connection: [Function: connectionListener] },
  httpAllowHalfOpen: false }
 >
undefined
> console.log('Server running at http://127.0.0.1:8124/
');
Server running at http://127.0.0.1:8124/
Undefined

可以通过浏览器访问该应用,这与用Node运行程序文件没有差别。而且,从REPL返回的response如上述粗体文本所示。

事实上,REPL最实用之处在于快捷查看对象。例如,Node核心对象global在Node.js官网上文档很少。为了更好地了解该对象,在REPL中将global对象传递给console.log方法,如下:

> console.log(global)

下面这行代码具有相同的结果:

> gl = global;

这里不再复制REPL中的运行结果。global对象接口非常多,你可以安装后自己尝试。这个练习的关键在于告知一种可以在任何时候简单快捷地查看对象接口的方法,不必去死记硬背需要调用什么方法、什么属性是可用的。

提示:
更多关于global对象请见第3章。
在REPL中可以使用上下箭头遍历之前输入的命令。这样可以很方便地查看之前的操作,也可以一定程度上提供编辑历史命令的能力。

阅读REPL中的如下代码:

> var myFruit = function(fruitArray,pickOne) {
... return fruitArray[pickOne - 1];
... }
undefined
> fruit = ['apples','oranges','limes','cherries'];
[ 'apples',
 'oranges',
 'limes',
 'cherries' ]
> myFruit(fruit,2);
'oranges'
> myFruit(fruit,0);
undefined
> var myFruit = function(fruitArray,pickOne) {
... if (pickOne <= 0) return 'invalid number';
 ...return fruitArray[pickOne - 1];
 ... };
undefined
> myFruit(fruit,0);
 'invalid number'
> myFruit(fruit,1);
'apples'

在以上输入中没有体现出来的是,每次在修改方法检查输入值时,首先向上查找之前的命令找到函数定义,回车重新运行该方法。每添加新的语句,再用方向键重复上述操作直到完成该函数。同时也使用向上方向键重复函数调用,输出undefined。

看起来似乎多做了很多工作只是为了避免重复输入,但是如果使用正则表达式,如以下例子:

> var ssRe = /^\d{3}-\d{2}-\d{4}$/;
undefined
> ssRe.test('555-55-5555');
true
> var decRe = /^\s*(\+|-)?((\d+(\.\d+)?)|(\.\d+))\s*$/;
undefined
> decRe.test(56.5);
true

则在写出一个正确的正则表达式之前通常要反复修改好几次(我并不擅长正则表达式)。用REPL做正则表达式的测试非常便捷,可以避免重复输入很长的正则表达式的痛苦。

幸运的是在REPL中只需要使用方向键就可以找到创建正则表达式的命令,修改,回车然后继续测试。

除了方向键,还可以使用Tab键自动补全。例如,在命令行中输入va,按Tab键,REPL会自动补全为var。Tab也可以用于自动补全任意的全局或者局部变量。表2-1列出了一些REPL中的按键功能。


fc13444152655ca8e8b2a58f1d86af19ae7cd650

如果你担心花很多时间在REPL中编程但是结束时却没有什么可以保存下来的文件,不用担心,.save命令可以保存当前的上下文输入。这一命令和其他REPL的命令会在下一节中讲到。

2.3.1 REPL命令

REPL提供了一些常用命令的接口。在前一节中提到了.save命令,该命令将当前语境中的输入保存在文件中。除非特意创建了一个新的语境或者使用.clear命令,该文件会包含在当前REPL中所有的输入:

> .save ./dir/session/save.js

保存下来的只有你自己直接输入的文本,就像在文本编辑器中直接输入的一样。

以下是一个完整的REPL命令以及功能列表:

.break

如果多行输入发生混乱不知道当前位置时,使用.break会重新开始。不过会丢失之前输入的多行内容。

.clear

重置语境并清空所有表达式。该命令可以使你重头再来。

.exit

退出REPL。

.help

显示所有可用的REPL命令。

.save

将当前REPL会话保存至文件。

.load

将文件加载到当前会话(.load/path/to/file.js)。

如果使用REPL作为开发应用程序的编辑器,以下提示或许会有帮助:

经常使用.save命令保存当前工作。尽管当前命令可以在历史记录中查找,但是重建代码依然是个很痛苦的过程。

提到关于命令的记录,接下来就会涉及如何定制自己的REPL。

2.3.2 REPL和rlwrap

Node.js官网上关于REPL的文档提到过设置环境变量,所以可以在REPL中使用rlwrap。那么,rlwrap是什么?又为什么要在REPL中使用rlwrap呢?

rlwrap将GNU readline库的功能添加至命令行,增加键盘输入的灵活性。它监听键盘输入并提供更多的功能,比如增强行编辑以及提供命令历史浏览功能。

需要安装rlwrap和readline以便在REPL中使用这一功能,大部分Unix系统提供了简单的包安装。比如,在我的Ubuntu系统中,安装rlwrap只需要一行命令:

apt-get install rlwrap

Mac用户可以使用自己的安装工具安装该程序。Windows用户需要使用Unix环境模拟器,比如Cygwin。

下面是一个简单的示例,如何在REPL中使用rlwrap将REPL的提示改为紫色:

env NODE_NO_READLINE=1 rlwrap -ppurple node

如果希望REPL的提示符一直是紫色的,可以在bashrc文件中添加别名(alias):

alias node="env NODE_NO_READLINE=1 rlwrap -ppurple node"

同时改变提示符和颜色,命令如下:

env NODE_NO_READLINE=1 rlwrap -ppurple -S "::>" node

现在提示符变为以下符号并且是紫色的:

::>

rlwrap组件的特殊之处在于它在多个REPL窗口中浏览命令历史的功能。REPL默认只能浏览当前REPL会话的命令历史,但是使用rlwrap,在关闭当前会话下一次重新进入REPL时,不仅可以浏览当前会话的历史命令,并且可以浏览之前会话的命令历史(以及其他命令行输入)。在下面例子中,显示的命令行不是手动输入的,而是通过方向键从命令历史中找出来的:

# env NODE_NO_READLINE=1 rlwrap -ppurple -S "::>" node
::>e = ['a','b'];
 [ 'a', 'b' ]
::>3 > 2 > 1;
false

即使rlwrap如此强大,但是每次输入无返回值的表达式时依然得到undefined。然而,这一现象是可以改变的,这就是下一节中将要讨论的功能——创建自定义的REPL。

2.3.3 定制REPL

Node提供了定制REPL的功能。为了实现该功能,首先需要引入REPL模块(repl):

var repl = require("repl");

通过在repl对象上调用start方法创建新的REPL。调用该方法的语法是:

repl.start([prompt], [stream], [eval], [useGlobal], [ignoreUndefined]);

所有参数都是可选择的。如果不传入参数,将会使用各参数的默认值,参数列表如下:

prompt

Default is>.默认值为>。

stream

Default is process.stdin.默认值为process.stdin。

eval

Default is the async wrapper for eval.eval的默认值为async。

useGlobal

默认值为false,新建一个语境而不是使用全局对象。

ignoreUndefined

默认值为false。不要忽略undefined的返回值。

当我开始慢慢厌倦REPL在无返回值的表达式输出undefined时,我决定创建自己的REPL。事实上只需要两行代码就可以实现(不包括注释):

repl = require("repl");
//设置ignoreUndefined为true,启动REPL
repl.start("node via stdin> ", null, null, null, true);

在Node中运行repl.js文件:

node repl.js

然后就可以像使用REPL内建版本一样使用自定义的REPL,除了自定义的提示符以及不会在变量赋值之后再看到讨厌的undefined之外,依然可以看到除了undefined之外的其他输出。

node via stdin> var ct = 0;
node via stdin> ct++;
0
node via stdin> console.log(ct);
1
node via stdin> ++ct;
2
node via stdin> console.log(ct);
2

在代码中我希望除了提示符和ignoreUndefined以外都使用默认值。设置其他参数为null会使Node使用该参数的默认值。

可以用自定义的REPL替换eval方法。唯一的要求是有具体的格式:

function eval(cmd, callback) {
  callback(null, result);
}

stream选项比较有意思。可以运行多个版本的REPL,从标准输入(默认)或者socket中获取输入内容。Node.js网站提供的REPL文档中给出了REPL监听TCP socket的例子,代码与下面这个例子类似:

var repl = require("repl"),
   net = require("net");
//设置ignoreUndefined为true,启动REPL
repl.start("node via stdin> ", null, null, null, true);
net.createServer(function (socket) {
 repl.start("node via TCP socket> ", socket);
}).listen(8124);

在Node中运行应用程序的时候会看到标准输入提示符。还可以通过TCP进入REPL。我使用PuTTY作为Telnet客户端来登录支持TCP版本的REPL,某种程度上来说是可行的。我必须先运行.clear清理样式,但在尝试使用下划线表示上一行命令的时候,Node无法解析该符号,如图2-1所示。


308f0f474a17d0006a4776a35ed183100cb46659

同样我也尝试过Windows 7 Telnet客户端,结果更糟糕。只有在Linux Telnet客户端使用时没有任何问题。

你可能预计到问题在于Telnet客户端的设置。然而,我并没有深究这一问题。因为从不安全的Telnet Socket运行REPL并不是我计划要做的事情,也并不推荐这一存在安全隐患的行为。就像在客户端代码中使用eval()一样,并没有破坏或者泄露客户发给你需要运行的内容,但是结果比这样更糟糕。

可以用UNIX socket通信运行REPL,比如GNU Netcat:

nc -U /tmp/node-repl-sock

可以像使用stdin一样输入命令。但是需要了解的是,如果使用TCP或者UNIX socket,任何console.log命令都会在server端打印输出,而不是客户端。

console.log(someVariable);//在server端输出

我想到一个很有用的应用程序,创建一个REPL程序,可以预加载模块。示例2-1的应用中,在REPL启动之后,http、os和util模块被加载并赋值给当前语境的对应属性。

示例2-1 创建可以预加载模块的自定义REPL

var repl = require('repl');
var context = repl.start(">>", null, null, null, true).context;
// 预加载模块
context.http = require('http');
context.util = require('util');
context.os = require('os');
用Node运行该程序,显示REPL的提示符,可以访问之前加载的那些模块:

>>os.hostname();
'einstein'
>>util.log('message');
5 Feb 11:33:15 - message
>>

如果希望像Linux中的可执行程序一样运行REPL程序,将下行代码加入应用程序开头:

#!/usr/local/bin/node
修改文件权限为可执行并运行:

# chmod u+x replcontext.js
# ./replcontext.js
 >>

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Node.js学习笔记(五、事件循环)
Node.js学习笔记(五、事件循环)
33 0
javascript权威指南学习笔记(第二章语法结构)
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hans201507/article/details/71439789 javascript是用Unicode字符集编写的,区分大小写,关键字、变量、函数名和所有的标识符都必须采取一致的大小写形式。
860 0
ts-node 学习笔记 - 如何解决在 Windows10 下不能直接运行 ts-node 的问题
ts-node 学习笔记 - 如何解决在 Windows10 下不能直接运行 ts-node 的问题
32 0
Node.js学习笔记(八、Stream流)
Node.js学习笔记(八、Stream流)
29 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
19696 0
Node.js学习
《了不起的Node.js:将JavaScript进行到底》(电子工业出版社) 2009年ryan在JavaScript开发者大会宣布了一个名为node.js的新技术,运行在服务器端的JavaScript,“以后开发web应用就只需要一种语言了!!!” node.js快速高效的优点得益于事件轮询技术(event loop),以及google为chrome浏览器设计的V8(JavaScript解释器和虚拟机)。
1181 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
17986 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
24794 0
+关注
异步社区
异步社区(www.epubit.com)是人民邮电出版社旗下IT专业图书旗舰社区,也是国内领先的IT专业图书社区,致力于优质学习内容的出版和分享,实现了纸书电子书的同步上架,于2015年8月上线运营。公众号【异步图书】,每日赠送异步新书。
12049
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
OceanBase 入门到实战教程
立即下载
阿里云图数据库GDB,加速开启“图智”未来.ppt
立即下载
实时数仓Hologres技术实战一本通2.0版(下)
立即下载