第1章 1.2 REPL 与脚本模式
上章回顾:上一章我们搞懂了 Node.js 是什么(一个用 JavaScript 写后台的运行环境),还成功把它装到了电脑上。现在你电脑里应该已经有个能跑的 Node.js 了。
本章目标:这一章我们要学两件事——怎么用 REPL 快速试验代码,怎么用脚本模式跑完整的 .js 文件。学完你就能真正写出一个可运行的 Node.js 程序了。
🎯 开场 3 分钟:为什么要学这个?
场景引入
想象一下:你刚学做菜,正确的学习方式是:
- REPL 模式 = 先在厨房里一样一样调料都尝尝,知道"哦,这个是咸的、那个是辣的"
- 脚本模式 = 最后做一盘完整的菜,端上桌给人吃
你学 Python/JS 这种解释型语言也是一样的道理。
痛点
我见过太多新手踩这个坑:
「我想试试
console.log('你好')能跑不,结果在 Node 官网找了半天不知道点哪里...」
或者:
「我写了个
test.js文件,双击打不开,拖到 Node 图标上也行不通,这玩意儿到底怎么跑?」
学完能解决
- 3 秒内验证一个 JavaScript 想法(不用建文件)
- 跑一个完整的 .js 脚本文件
- 读取命令行传入的参数,做出「可配置」的小工具
🧱 基础 25 分钟:核心概念
1.2.1 REPL 是什么?
REPL = Read-Eval-Print-Loop,翻译成人话就是「读取 → 执行 → 打印 → 循环」。
生活类比
就像你用计算器:
1. 你输入 2+2(Read)
2. 计算器算出 4(Eval)
3. 屏幕显示 4(Print)
4. 等待你输入下一道题(Loop)
REPL 就是 Node.js 自带的「计算器」,让你一句一句试 JavaScript。
怎么打开?
打开终端(Mac 是「终端」App,Windows 是「命令提示符」或 PowerShell),输入:
node
看到 > 符号了吗?恭喜你进入 REPL 了:
~
❯ node
Welcome to Node.js v20.0.0.
Type ".help" for more information.
>
第一个 REPL 命令
在 > 后面输入:
> console.log('你好,世界')
你好,世界
undefined
解释一下:console.log() 是打印函数,它会输出「你好,世界」,然后返回 undefined(这是 JavaScript 函数的默认值,下一章会细讲)。
再试一个数学计算:
> 3 + 5
8
> const 名字 = '小明'
undefined
> 名字 + '喜欢吃苹果'
'小明喜欢吃苹果'

退出 REPL
按 Ctrl + C 两次,或者输入 .exit:
> .exit
~
常用 REPL 命令
| 命令 | 作用 |
|---|---|
.help |
显示帮助 |
.exit |
退出 |
.clear |
清屏 |
.save 文件名 |
把当前会话保存为文件 |
.load 文件名 |
加载一个文件到 REPL |
1.2.2 脚本模式:跑一个 .js 文件
REPL 适合尝鲜,但你想写一个「以后还能用」的程序,就得用脚本模式。
步骤
第一步:创建文件
用任意文本编辑器(VS Code、记事本都行),新建一个文件叫 hello.js,内容:
// 这是我的第一个 Node.js 脚本
console.log('你好,世界!');
// 算个数学题
const a = 10;
const b = 20;
console.log('10 + 20 =', a + b);
第二步:运行
在终端里,进入文件所在目录,然后:
node hello.js
输出:
你好,世界!
10 + 20 = 30
敲黑板
- 文件后缀必须是
.js(Node.js 认识这个格式) - 运行命令是
node 文件名(不是node.exe 文件名.js,别搞混) - 双击 .js 文件是打不开的(除非你装了 VS Code 的「Code Runner」插件)

1.2.3 process.argv:让脚本接受参数
这是本章最实用的知识点之一。
痛点
你想写一个程序,输出「你好,张三」,但张三这个名字每次可能不一样。你会怎么办?
难道每次都去改代码?太麻烦了!
解决方案:命令行参数
Node.js 有一个内置对象 process(进程),里面有个 argv 数组(argument values)。
创建一个文件 greet.js:
// process.argv 是个数组
// argv[0] = node 的路径
// argv[1] = 当前脚本的路径
// argv[2] = 你传入的第一个参数
// argv[3] = 第二个参数...以此类推
const 名字 = process.argv[2] || '路人甲';
console.log('你好,' + 名字 + '!');
运行:
node greet.js 小明
输出:
你好,小明!
再试:
node greet.js
输出:
你好,路人甲!
这里用了 || '路人甲',意思是「如果没传参数,默认用路人甲」。
工作原理图解
命令: node greet.js 小明
process.argv 数组:
┌─────────────────────────────────┐
│ argv[0] │ "node 的安装路径" │
│ argv[1] │ "greet.js 的路径" │
│ argv[2] │ "小明" │ ← 我们用的就是这个
└─────────────────────────────────┘
1.2.4 小明购物清单实战
还记得上一章提到的小明吗?他有一张购物清单:
// shopping.js
const 清单 = ['苹果', '香蕉', '牛奶', '面包'];
// 用 for 循环遍历
for (let i = 0; i < 清单.length; i++) {
console.log('要买:' + 清单[i]);
}
运行 node shopping.js:
要买:苹果
要买:香蕉
要买:牛奶
要买:面包
加点功能:按条件筛选
小明今天只想买水果(苹果、香蕉),用 if 判断:
// shopping.js
const 清单 = ['苹果', '香蕉', '牛奶', '面包'];
const 关键词 = process.argv[2] || '水果';
console.log('小明的购物清单(筛选:' + 关键词 + ')');
console.log('---');
for (let i = 0; i < 清单.length; i++) {
// 判断当前物品是否包含关键词
if (清单[i].includes(关键词) || 关键词 === '全部') {
console.log('✓ ' + 清单[i]);
}
}
运行:
node shopping.js 水果
小明的购物清单(筛选:水果)
---
✓ 苹果
✓ 香蕉
node shopping.js 全部
小明的购物清单(筛选:全部)
---
✓ 苹果
✓ 香蕉
✓ 牛奶
✓ 面包
🔥 实战 35 分钟:3 个递进项目
项目 1:单位转换器(5 分钟)
场景:小明在美国网站看到一件衣服标注尺寸是 32 英寸,他想知道是多少厘米。
代码 convert.js:
// 单位转换器
const 数值 = parseFloat(process.argv[2]);
const 类型 = process.argv[3];
if (!数值 || !类型) {
console.log('用法:node convert.js 数值 类型');
console.log('类型:inch(英寸转厘米)、cm(厘米转英寸)');
process.exit(1);
}
if (类型 === 'inch') {
const 结果 = 数值 * 2.54;
console.log(数值 + ' 英寸 = ' + 结果 + ' 厘米');
} else if (类型 === 'cm') {
const 结果 = 数值 / 2.54;
console.log(数值 + ' 厘米 = ' + 结果.toFixed(2) + ' 英寸');
} else {
console.log('未知类型:' + 类型);
}
运行:
node convert.js 32 inch
输出:
32 英寸 = 81.28 厘米
解释:.toFixed(2) 是让小数保留 2 位,看起来更舒服。
项目 2:待办清单管理器(15 分钟)
场景:小明每天有一堆事要做,想用命令行管理待办事项,存在文件里。
代码 todo.js:
const fs = require('fs');
const 文件名 = 'todo.json';
// 读取待办事项
function 读取待办() {
if (fs.existsSync(文件名)) {
const 内容 = fs.readFileSync(文件名, 'utf-8');
return JSON.parse(内容);
}
return [];
}
// 保存待办事项
function 保存待办(待办列表) {
fs.writeFileSync(文件名, JSON.stringify(待办列表, null, 2));
}
// 添加待办
function 添加待办(任务) {
const 待办列表 = 读取待办();
待办列表.push({
任务: 任务,
完成: false,
时间: new Date().toLocaleString('zh-CN')
});
保存待办(待办列表);
console.log('已添加:' + 任务);
}
// 列出待办
function 列出待办() {
const 待办列表 = 读取待办();
if (待办列表.length === 0) {
console.log('待办清单是空的!');
return;
}
console.log('=== 小明的待办清单 ===');
待办列表.forEach((item, index) => {
const 状态 = item.完成 ? '✓' : '□';
console.log((index + 1) + '. [' + 状态 + '] ' + item.任务);
});
}
// 删除待办
function 删除待办(序号) {
const 待办列表 = 读取待办();
if (序号 > 0 && 序号 <= 待办列表.length) {
const 已删除 = 待办列表.splice(序号 - 1, 1);
保存待办(待办列表);
console.log('已删除:' + 已删除[0].任务);
} else {
console.log('序号无效');
}
}
// 主逻辑
const 操作 = process.argv[2];
if (操作 === 'list') {
列出待办();
} else if (操作 === 'add') {
const 任务 = process.argv[3];
if (任务) {
添加待办(任务);
} else {
console.log('请输入任务内容:node todo.js add "买牛奶"');
}
} else if (操作 === 'delete') {
const 序号 = parseInt(process.argv[3]);
if (!isNaN(序号)) {
删除待办(序号);
} else {
console.log('请输入有效序号');
}
} else {
console.log('用法:');
console.log(' node todo.js list - 查看清单');
console.log(' node todo.js add "买牛奶" - 添加任务');
console.log(' node todo.js delete 1 - 删除任务');
}
运行示例:
node todo.js add "写代码"
node todo.js add "给妈妈打电话"
node todo.js list
node todo.js delete 1
node todo.js list
输出:
已添加:写代码
已添加:给妈妈打电话
=== 小明的待办清单 ===
1. [□] 写代码
2. [□] 给妈妈打电话
已删除:写代码
=== 小明的待办清单 ===
1. [□] 给妈妈打电话
解释:
- fs 是 Node.js 内置的文件系统模块(下一章会详细讲模块)
- 数据存在 todo.json 文件里,关闭程序再打开还在
- 用 JSON.stringify(待办列表, null, 2) 让 JSON 格式化输出,方便阅读
项目 3:简易日志分析器(15 分钟)
场景:小明是一个小电商运营,他有一份订单日志,想分析出今天的销售额和订单数。
准备:先创建 orders.log 文件:
订单编号,金额,商品
A001,99.5,手机壳
A002,299,蓝牙耳机
A003,59.9,数据线
A004,1999,移动电源
A005,45,手机膜
代码 analyzer.js:
const fs = require('fs');
// 读取文件
const 文件路径 = process.argv[2];
if (!文件路径) {
console.log('用法:node analyzer.js 订单日志文件路径');
process.exit(1);
}
if (!fs.existsSync(文件路径)) {
console.log('文件不存在:' + 文件路径);
process.exit(1);
}
const 内容 = fs.readFileSync(文件路径, 'utf-8');
const 行数组 = 内容.trim().split('\n');
// 跳过表头,从第二行开始
const 订单列表 = 行数组.slice(1);
let 总金额 = 0;
let 订单数 = 0;
console.log('=== 订单分析报告 ===\n');
订单列表.forEach((行, index) => {
const 字段 = 行.split(',');
const 编号 = 字段[0];
const 金额 = parseFloat(字段[1]);
const 商品 = 字段[2];
总金额 += 金额;
订单数++;
console.log((index + 1) + '. ' + 编号 + ' | ' + 商品 + ' | ¥' + 金额);
});
console.log('\n--- 统计 ---');
console.log('总订单数:' + 订单数);
console.log('总销售额:¥' + 总金额.toFixed(2));
console.log('平均客单价:¥' + (总金额 / 订单数).toFixed(2));
运行:
node analyzer.js orders.log
输出:
=== 订单分析报告 ===
1. A001 | 手机壳 | ¥99.5
2. A002 | 蓝牙耳机 | ¥299
3. A003 | 数据线 | ¥59.9
4. A004 | 移动电源 | ¥1999
5. A005 | 手机膜 | ¥45
--- 统计 ---
总订单数:5
总销售额:¥2502.40
平均客单价:¥500.48
解释:
- split('\n') 把多行文本拆成数组
- split(',') 把 CSV 格式的每一行拆成字段
- .toFixed(2) 确保金额显示两位小数
💪 进阶 20 分钟:常见坑 + 技巧
坑 1:process.argv 索引从 2 开始
// ❌ 错误:以为 argv[1] 是第一个参数
const 名字 = process.argv[1];
// ✅ 正确:前两个是 node 路径和脚本路径,从 2 开始才是用户输入
const 名字 = process.argv[2];
坑 2:忘了处理文件不存在
// ❌ 错误:直接读文件,不检查存不存在
const 内容 = fs.readFileSync('todo.json', 'utf-8');
// ✅ 正确:先检查再读
if (fs.existsSync(文件名)) {
const 内容 = fs.readFileSync(文件名, 'utf-8');
} else {
console.log('文件不存在,将创建新文件');
}
坑 3:JSON.parse 遇到空文件
// ❌ 错误:空文件会报错
const 数据 = JSON.parse(fs.readFileSync('empty.json', 'utf-8'));
// ✅ 正确:加上 try-catch 或者确保文件有内容
try {
const 数据 = JSON.parse(fs.readFileSync('empty.json', 'utf-8') || '[]');
} catch (e) {
const 数据 = [];
}
坑 4:中文乱码(Windows 常见)
// Windows 用户如果输出乱码,尝试指定编码
const 内容 = fs.readFileSync('文件.txt', 'utf-8');
坑 5:数字字符串直接运算
// ❌ 错误:字符串相加会变成拼接
const a = '10';
const b = '20';
console.log(a + b); // 输出 "1020"
// ✅ 正确:转成数字
const a = parseFloat('10');
const b = parseFloat('20');
console.log(a + b); // 输出 30
调试技巧:console.log 虽土但有用
// 在关键步骤加日志,看看变量实际是什么
console.log('调试:待办列表 =', 待办列表);
console.log('调试:argv =', process.argv);
✏️ 练习题
练习 1(2 分钟):改改改
把 greet.js 改成向小明说晚安,而不是问好。
- 输入:
node greet.js 小明 - 预期输出:
晚安,小明!
练习 2(3 分钟):加个判断
修改 convert.js,当用户输入负数时,提示「数值不能为负」并退出。
- 输入:
node convert.js -5 inch - 预期输出:
数值不能为负
练习 3(5 分钟):新数据
创建一个新的日志文件 sales.log,包含你自己的 3 条「销售记录」(格式:编号,金额,商品),用 analyzer.js 分析它。
- 输入:自定义日志文件
- 预期输出:统计报告
练习 4(10 分钟):串起来
把项目 2(待办清单)和项目 3(日志分析)结合起来:当日志分析完成后,把分析结果自动添加到待办清单里。
- 提示:需要调用
添加待办()函数
练习 5(5 分钟):报错分析
小明运行这段代码:
node todo.js add
报错了:
请输入任务内容:node todo.js add "买牛奶"
这是 bug 还是正常行为?小明应该怎么输入才对?
作业:做一个命令行「体重记录工具」
需求描述:
做一个能记录体重、查看历史、计算平均值的命令行工具。
功能点:
1. 添加体重记录(带日期)
2. 查看所有记录
3. 计算平均体重
4. 删除指定记录
加分项:
1. 支持按日期范围筛选
2. 能画个简单的 ASCII 图表显示趋势
验收标准:
- node weight.js add 65.5 能添加一条记录
- node weight.js list 能显示所有记录
- node weight.js avg 能显示平均体重
- 数据保存到 weight.json 文件
📚 总结
本章核心 3 点
- REPL = 交互式试验场,适合快速验证想法
- 脚本模式 =
node 文件名.js,跑完整的程序 - process.argv = 让程序接收命令行参数,做成「可配置工具」
延伸资源
互动钩子
你有没有遇到过「不知道该用 REPL 还是脚本模式」的情况?或者你在工作/学习中用过 Node.js 做什么有意思的小工具?评论区聊聊,老粉优先回复!
下章预告:
学会了怎么跑代码,小明信心满满地写了一个 app.js,里面引用了另一个文件 utils.js,结果跑起来报错 Cannot find module...下一章我们来解决这个问题,一起搞懂 Node.js 的模块系统 CommonJS!

评论(0)