B 币安 · 全球最大的加密货币交易所币安 注册 → AD OKX OKX · 全球领先的加密货币交易所OKX 注册 → AD
💻 编程基础 · 第10讲 / 10

阅读并检验 AI 写的代码——测试、安全、许可证与个人信息

AI 写的代码看起来像模像样,却可能在边界情况下出错,或者存在安全风险。阅读代码、用自己设计的测试来确认、检查机密信息、软件包、许可证和个人信息,这些都是使用代码的人的责任。

⏱ 约24分钟 ✍️ 练习题 5 道 🧪 代码练习 4 题 更新 2026-10-09
🎯 本讲学习目标
  • 能按输入、处理、输出的顺序逐行阅读并解释 AI 写的函数
  • 能亲手设计包含空值、0和边界值的测试,找出并修正隐藏的错误
  • 能识别泄露机密信息、缺少输入验证、eval、不存在的软件包等安全风险
  • 能说出为遵守开源许可证条件和保护个人信息需要确认哪些事项

1.为什么要检验 AI 写的代码

AI 编程助手几秒钟就能写出像样的代码,语法整洁,解释也显得很有把握。但 AI 生成的是“常见样子的代码”,并不是在你的数据、你的情况下实际运行之后给出的答案。所以,它可能给出在普通输入下正确、遇到空值或边界值就出错的代码,也可能给出能运行但有风险的代码。

运行代码并使用结果的人是你自己。销售合计算错导致报告出错,或者含有密码的代码被公开,都不能用“是 AI 这么写的”来搪塞。核对结果、承担责任的,是使用这段代码的人和组织。到第9讲为止学过的值、条件、循环、函数、数组和字符串知识,正是做这种检验的工具。只要自己能读懂短小的代码,就能更安全、更好地使用 AI。

检验大致分四个方面:读懂代码在做什么;用自己设计的测试确认结果;检查安全风险;确认许可证、个人信息这类代码之外的问题。这一讲就按这个顺序逐一练习。

  • 阅读:这个函数接收什么、返回什么
  • 测试:在普通输入、空值、0、边界值下,是否得到预期的值
  • 安全:有没有机密信息、未经验证的输入、eval、陌生的软件包
  • 代码之外:是否遵守了许可证条件、公司或学校的规定以及个人信息保护

2.逐行阅读——输入、处理、输出

阅读 AI 给的代码时,不要从头到尾一扫而过,而是套用第1讲学过的“输入 → 处理 → 输出”框架。先看函数名和参数,弄清它接收什么;再找到 return,弄清它返回什么。然后逐行查看中间的代码,用语言说出“这一行执行完后,变量里装的是什么”。说不下去的那一行,正是需要确认的地方。

边读边问“如果……会怎样”:数组是空的呢?值以字符串形式传进来呢?是0或负数呢?下面的平均值函数在普通分数下是对的,但遇到空数组就是 0 / 0,得到 NaN;如果传进来的是从 CSV 里取出的字符串分数,+ 就变成拼接,算出离谱的值。它连错误信息都没有就给出错误的值,所以更危险。

遇到看不懂的语法,也可以问 AI“请给初学者解释一下这一行在做什么”。不过听完解释后,要按第8讲学的方法用 console.log 打印中间值,亲自确认解释和实际情况是否一致。解释也是 AI 的回答,同样需要确认。

普通输入是对的。遇到空数组和字符串分数时,不报错却给出错误的值。
// AI 编程助手写的函数(原样)
function averageScore(scores) {
  let sum = 0;
  for (const s of scores) {
    sum += s;
  }
  return sum / scores.length;
}

console.log(averageScore([80, 90, 100]));
console.log(averageScore([]));
console.log(averageScore(["80", "90"]));
输出
90
NaN
4045

3.亲手设计测试——边界值能找出错误

测试就是事先写下“输入这个,就应该得到这个值”,再用代码来确认。预期值要在看代码之前由你亲手定下来。如果连测试也交给 AI,它可能把代码里的同一个误解也写进测试,所以至少要亲手写几个。像下面的 check 函数这样,把得到的值和预期值比较后打印“通过/失败”,有这样一个小工具就足够了。

好的测试集合除了普通情况,还要包含边界情况:空数组、空字符串、0、负数、只有一个值的情况、条件刚好发生变化的那个值(第3讲的边界值)、位数不同的数、多个值相同的情况等等。错误往往就藏在这些边缘处。

下面是“AI 写的取最高分函数”。只用两位数的分数测试时会通过,看起来是正确的代码。可是一混进100分就出错了。JavaScript 的 sort() 如果不给比较函数,会把值转成字符串按字典顺序排序,所以 "100" 会排在 "80" 前面。而且 sort 会改变原数组的顺序,连调用这个函数的一方的数据也被悄悄改掉了。

如果只测两位数的分数,就发现不了这个错误。原数组也被改变了。
function check(name, got, want) {
  const same =
    JSON.stringify(got) === JSON.stringify(want);
  console.log(same ? "通过" : "失败", name, got);
}

// AI 写的函数:取最高的 n 个分数
function topScores(scores, n) {
  return scores.sort().reverse().slice(0, n);
}

check("两位数", topScores([70, 90, 85], 2), [90, 85]);
check("100分", topScores([100, 95, 80], 2), [100, 95]);
check("n为0", topScores([70, 90], 0), []);

const mine = [70, 90, 85];
topScores(mine, 1);
console.log("原数组", mine);
输出
通过 两位数 [ 90, 85 ]
失败 100分 [ 95, 80 ]
通过 n为0 []
原数组 [ 90, 85, 70 ]
用数字比较函数 (a, b) => b - a 对副本排序,从大到小排列。
function topScores(scores, n) {
  const copy = [...scores];
  copy.sort((a, b) => b - a);
  return copy.slice(0, n);
}

const mine = [100, 95, 80];
console.log(topScores(mine, 2));
console.log(topScores([], 3));
console.log("原数组", mine);
输出
[ 100, 95 ]
[]
原数组 [ 100, 95, 80 ]
例题AI 写了一个“运费函数”,规则是订单金额在50,000韩元及以上免运费,否则收3,000韩元。应该放进哪些测试?
// AI 写的函数(原样)
function shippingFee(price) {
  return price > 50000 ? 0 : 3000;
}
for (const p of [49999, 50000, 50001]) {
  console.log(p, shippingFee(p));
}
输出
49999 3000
50000 3000
50001 0
  1. 第1步 从规则中找出边界。条件发生变化的值是50,000韩元。
  2. 第2步 选出边界的前一个、边界本身和后一个:49,999韩元 → 3,000,50,000韩元 → 0,50,001韩元 → 0。
  3. 第3步 再加上极端值和奇怪的值:先定好输入0韩元、负数、字符串 "50000" 时应该怎样处理。
  4. 第4步 运行后发现,50,000韩元时得到的是3,000。读代码才发现写的是 > 而不是 >=。
答案把边界值50,000韩元及其前后的值,以及0、负数、字符串放进测试。在这个例子里,边界值测试发现了需要把 > 改成 >=。
向 AI 要代码时,顺便请它“也给出这个函数的边界情况测试清单”。在这份清单上加入自己想到的情况,预期值则由自己亲手来定。

4.安全——机密信息、输入验证、`eval`

首先要遵守的原则是:不要把密码、API 密钥、访问令牌这类机密信息写进代码或提示词。写在代码里的密钥,很容易因为共享文件、公开代码仓库或截图而泄露出去。向 AI 助手询问错误时把含有密钥的代码整段粘贴进去,也是同样的风险。密钥的位置要换成 "YOUR_API_KEY" 这样的假值再给别人看,真正的密钥则按各服务指引的方式(环境变量、配置文件、密钥管理功能等)放在代码之外。如果密钥已经泄露,光删除是不够的,要在该服务中作废密钥并重新申请。

第二是输入验证。来自用户输入、文件或其他程序的值,都要当作随时可能和预想的不一样,在使用前确认它的样子和范围。AI 写的代码常常只假设“正常的输入”。像下面这样先定好允许的样子,其余的值一律返回 null 拒绝,是比较安全的做法。“只接受好的值”比“挑出坏的值”更不容易遗漏。

第三,不能把不可信的输入交给 eval 或 new Function。eval 会把字符串当作代码执行,输入里藏着的命令可能就这样被执行了。如果你请 AI 写计算器或读取设置,它却给了用 eval 的代码,就请它换一种方法。如果目的是交换数据,用第6讲的 JSON,通过 JSON.parse 读取即可。JSON.parse 只读取数据,不执行代码,格式不对时会报错。

正则表达式 ^\d+$ 的意思是“从头到尾只有数字”。允许范围之外的一律是 null。
// 只接受0~150之间的整数年龄
function parseAge(input) {
  const s = String(input).trim();
  if (!/^\d+$/.test(s)) return null;
  const n = Number(s);
  if (n > 150) return null;
  return n;
}

const tries = ["35", " 7 ", "-3", "2.5", "abc", "", "999"];
for (const x of tries) {
  console.log(JSON.stringify(x), "→", parseAge(x));
}
输出
"35" → 35
" 7 " → 7
"-3" → null
"2.5" → null
"abc" → null
"" → null
"999" → null
JSON.parse 不会执行看起来像代码的字符串,而是用 SyntaxError 拒绝。try { … } catch (e) { … } 这种语法让程序出错时不停下,而是在 catch 部分接住错误并处理。
const input = '{"name": "小明", "score": 90}';
const data = JSON.parse(input);
console.log(data.name, data.score);

try {
  JSON.parse("console.log('偷偷执行')");
} catch (e) {
  console.log("拒绝:", e.name);
}
输出
小明 90
拒绝: SyntaxError

5.不存在的软件包——安装前先确认

实际工作中的代码,经常要安装别人制作并公开的软件包(库)来使用。可是 AI 助手有时会编造听起来像样、实际上并不存在的包名,或者推荐早已停止维护的包。更大的问题是,有人可能抢先用这些常被编造的名字,或者和知名软件包只差一个字母的名字,上传恶意软件包。不加确认就照着执行安装命令,别人的代码就会在你的电脑上运行。

所以在执行安装命令之前,要确认几件事:这个软件包是否真的存在于官方软件包仓库,名字的拼写是否和官方文档完全一致,最近是否仍有人维护,是否有公开的源代码仓库和文档,是否被广泛使用。公司或学校可能另有允许使用的软件包清单或安全审查流程,要先遵守那些规定。

如果是小事,也可以问问能否不用软件包就解决。第9讲的文本整理和合计计算,只用 JavaScript 的基本功能就足够了。依赖的软件包越少,要确认的东西越少,可能出问题的地方也越少。不过,像读取真正的 CSV 文件、加密这类规则复杂的工作,与其自己写,不如选择经过验证的软件包更安全。

  • 官方软件包仓库里是否真的有这个名字
  • 拼写是否和官方文档完全相同(警惕只差一个字母的假名字)
  • 最近是否有更新、是否仍在维护
  • 源代码仓库、文档和许可证是否公开
  • 是否遵守了公司或学校的允许清单和安全流程

6.著作权、许可证与个人信息

公开的代码,并不等于可以随意使用。开源代码附带许可证,许可证是“遵守这些条件就可以使用”的约定。条件因许可证而异。常见的条件是保留著作权声明和许可证文本;有的许可证还要求,在发布包含该代码的程序时,以相同条件公开源代码。完全没有标明许可证的代码,即使公开了,也不一定意味着允许使用。

AI 生成的代码也有可能和现有的公开代码非常相似。在把长段代码原样放进产品之前,要看看能否确认出处,公司或学校有没有关于 AI 生成代码和使用开源软件的规定。下表只是常见类型的一般特点,并非法律建议。实际条件要读许可证原文,难以判断时,向所在机构的负责部门或专业人士确认。

个人信息也是代码之外的重要问题。请 AI 写代码时,如果把真实的客户名单、学生成绩、电话号码粘贴进去,这些信息就脱离了你的控制。写代码需要的是数据的“样子”,而不是真实的值。像下面这样做出假数据给 AI 看,真实数据只在自己的电脑上运行。示例地址使用 example.com 这类专为文档保留的域名,就不会联系到真实的人。分享结果时,只展示必要的部分,比如把电话号码中间几位遮住。

不用真实名单,而是做出格式相同的假数据给 AI 看。
function fakeCustomers(n) {
  const list = [];
  for (let i = 1; i <= n; i++) {
    const no = String(i).padStart(4, "0");
    list.push({
      id: i,
      name: "客户" + i,
      phone: "010-0000-" + no,
      email: "user" + i + "@example.com",
    });
  }
  return list;
}
console.log(fakeCustomers(2));
输出
[ { id: 1, name: '客户1', phone: '010-0000-0001', email: '[email protected]' }, { id: 2, name: '客户2', phone: '010-0000-0002', email: '[email protected]' } ]
开源许可证类型的一般特点(并非法律建议——请确认原文和所在机构的规定)
类型例子常见条件
宽松型MIT, BSD广泛允许复制、修改和商业使用。需保留著作权声明和许可证文本
宽松型(含专利条款)Apache-2.0和 MIT 一样广泛允许。需保留许可证副本和声明,在修改过的文件中注明改动,并含有与专利相关的条款
著佐权GPL 系列发布包含该代码的程序时,须以相同许可证提供源代码等,条件更严格
未标明没有许可证文件的公开代码即使公开,也不一定意味着允许使用。使用前要确认

7.把检验步骤变成习惯

拿到 AI 给的代码后,如果每次都按同样的顺序检查,就能减少遗漏。首先阅读代码,看你拜托的事和代码实际做的事是否一致;不懂的行,听完解释后用 console.log 确认。接着设计由自己定下预期值的测试,普通情况两三个、边界情况三四个,然后运行。

测试失败时,把失败的输入、得到的值和预期值整理好再去问 AI 就行。正如第8讲学过的,比起说“错了”,说“输入 [100, 95, 80],预期 [100, 95],实际 [95, 80]”能更快修好。拿到修改后的代码,要把最初的测试全部重新运行一遍,看看是否修好一处又弄坏了另一处。把修改前后的代码放进文本比较工具,就能准确看出改了什么。

最后做安全和代码之外的检查:是否还残留机密信息,是否验证了输入,有没有 eval,陌生的软件包确认过没有,是否遵守了许可证和个人信息方面的规定。经过这些步骤的代码,才是“自己能负责的代码”。编程基础课程到此结束。现在你已经能阅读、测试和修改短小的代码了,不妨让 AI 当助手,把重复的工作一件一件改写成代码。

  • 确认目的:拜托的事和代码做的事是否一致
  • 逐行阅读:输入、处理、输出,卡住的行用 console.log 确认
  • 测试:预期值由自己定,一定要加入边界值
  • 重新运行:修改之后,从头运行全部测试
  • 安全与代码之外:机密信息、输入验证、eval、软件包、许可证、个人信息

📌 要点总结

  • AI 的代码可能在常见输入下正确、在边界处出错——使用的人要负责检验
  • 先由自己定下预期值,把空值、0、边界值和位数不同的值放进测试
  • sort() 没有比较函数时会按字符串顺序排序,并且会改变原数组
  • 机密信息不写进代码和提示词,输入要验证,不可信的输入不用 eval
  • 确认软件包真实存在且有人维护,确认许可证条件和相关规定,用假数据代替个人信息

✍️ 练习题

请先自己作答,再展开“答案与解析”。

Q1. [100, 95, 80].sort() 的结果是什么?

⭕ 回答正确

❌ 再想一想

答案与解析
答案 ③ [100, 80, 95]

没有比较函数时,会把值转成字符串按字典顺序排序,顺序是 "100" < "80" < "95"。要按数字排序,需要像 sort((a, b) => a - b) 这样给出比较函数。

Q2. 测试“满5万韩元免运费”的函数时,最重要的测试值是哪个?

⭕ 回答正确

❌ 再想一想

答案与解析
答案 ② 50,000韩元

这是条件发生变化的边界值。把 > 和 >= 弄混的错误,恰恰只在50,000韩元时才会暴露。

Q3. 想向 AI 助手询问一个错误,但代码里有真实的 API 密钥。应该怎么做?

⭕ 回答正确

❌ 再想一想

答案与解析
答案 ② 把密钥换成 "YOUR_API_KEY" 这样的假值再粘贴

机密信息既不写进代码,也不放进提示词。如果已经泄露,要在服务中作废密钥并重新申请。

Q4. AI 让你安装一个从没见过的软件包。下面哪一项不适合作为安装前的确认事项?

⭕ 回答正确

❌ 再想一想

答案与解析
答案 ③ AI 说得是否很有把握

AI 也能很有把握地编造出不存在的包名。要亲自确认是否真实存在、拼写、维护状况以及所在机构的规定。

Q5. 想请 AI 写一段整理公司客户数据的代码。请写出既能保护个人信息又能提出请求的方法。

答案与解析
参考答案 不用真实数据,而是用几行格式相同的假数据(假名字、010-0000-0001、example.com 地址等)只展示格式,拿到的代码在自己的电脑上用真实数据运行。同时确认公司关于使用 AI 的规定。

写代码需要的是数据的样子。粘贴真实的值,这些信息就会脱离你的控制。

🧪 代码练习

用测试找出看似 AI 写的代码里隐藏的错误并修正,再写出输入验证和遮盖个人信息的函数。请先看测试结果中失败的输入。

代码只在本浏览器内的隔离环境中运行,不会发送到服务器。无法访问网络,超过2秒会自动停止。改过的代码只保存在此浏览器中。按 Ctrl+Enter(⌘+Enter)运行,Tab 键输入两个空格(先按 Esc 再按 Tab 可跳到下一项)。

浏览器脚本未开启,无法运行代码,但题目、初始代码、自动检查项和参考答案都可以直接阅读。

1修正平均值函数的边界

AI 写的 average(nums) 遇到空数组时返回 NaN。请修改为:空数组时返回0,其他情况返回平均值。数组里只会有数字。

自动检查
  • average([80,90,100])期望值 90
  • average([])期望值 0
  • average([0,0])期望值 0
  • average([-10,10,30])期望值 10
  • average([7])期望值 7
💡 提示

在函数开头,如果 nums.length === 0,就立刻 return 0;。

查看参考答案
function average(nums) {
  if (nums.length === 0) return 0;
  let sum = 0;
  for (const x of nums) {
    sum += x;
  }
  return sum / nums.length;
}

2最高分函数的排序错误

AI 写的 topScores(scores, n) 应该按从大到小的顺序返回最高的 n 个分数,但一混进100分就出错。请改成按数字大小排序,并且不要改变原数组。n 大于数组长度时,有多少就返回多少。

自动检查
  • topScores([70,90,85], 2)期望值 [90,85]
  • topScores([100,95,80], 2)期望值 [100,95]
  • topScores([5,100,20,9], 3)期望值 [100,20,9]
  • topScores([60,70], 0)期望值 []
  • topScores([], 3)期望值 []
  • topScores([88], 5)期望值 [88]
💡 提示

用 [...scores] 复制后,用 sort((a, b) => b - a) 从大到小排序,再 slice(0, n)。

查看参考答案
function topScores(scores, n) {
  const copy = [...scores];
  copy.sort((a, b) => b - a);
  return copy.slice(0, n);
}

3验证订购数量的输入

请完成函数 parseQuantity(input)。去掉字符串 input 的首尾空格后,如果是只由数字组成的整数,并且在1到99之间,就返回这个数字,否则返回 null。"2.5"、"-1"、"1e3" 和空字符串都返回 null。

自动检查
  • parseQuantity("3")期望值 3
  • parseQuantity(" 12 ")期望值 12
  • parseQuantity("0")期望值 null
  • parseQuantity("100")期望值 null
  • parseQuantity("2.5")期望值 null
  • parseQuantity("1e3")期望值 null
💡 提示

用 /^\d+$/.test(s) 检查是否只有数字,如果 n < 1 || n > 99 就返回 null。还要注意 Number("") 是0。

查看参考答案
function parseQuantity(input) {
  const s = input.trim();
  if (!/^\d+$/.test(s)) return null;
  const n = Number(s);
  if (n < 1 || n > 99) return null;
  return n;
}

4遮住电话号码中间几位

请写一个在分享结果时遮盖个人信息的函数 maskPhone(s)。如果 s 是 010-1234-5678 这样的3-4-4位数字格式,就把中间四位换成 ****,返回 010-****-5678;如果不是这个格式,就返回 null。

自动检查
  • maskPhone("010-1234-5678")期望值 "010-****-5678"
  • maskPhone("010-0000-0001")期望值 "010-****-0001"
  • maskPhone("01012345678")期望值 null
  • maskPhone("")期望值 null
  • maskPhone("010-12-5678")期望值 null
💡 提示

正则表达式 /^\d{3}-\d{4}-\d{4}$/ 只放行“3位-4位-4位数字”。不通过就返回 null。

查看参考答案
function maskPhone(s) {
  if (!/^\d{3}-\d{4}-\d{4}$/.test(s)) return null;
  const parts = s.split("-");
  return parts[0] + "-****-" + parts[2];
}

🤖 可以这样问 AI

复制后把 [ ] 部分换成你的情况。不要直接相信回答,请与正文对照核实。

想读懂 AI 给的代码时

请像给初学者讲解一样,逐行解释下面的 JavaScript 代码。用示例输入[示例输入]跟踪每一行执行完后主要变量里装的是什么,并指出传入空值、0或字符串时会发生什么。
[代码]

想不遗漏地找出要测试的情况时

下面这个函数的目的是[目的]。请分成普通情况、边界值、空值、错误输入几类,列出需要测试的输入清单。预期值我会自己定,所以只写输入和“为什么这个情况很重要”。
[函数代码]

想请人从安全和许可证角度检查代码时

请从安全角度检查下面的代码。把代码中残留的机密信息、未经验证的输入、`eval` 之类的危险功能,以及如何确认所用软件包真实存在且仍在维护,整理成清单。如果有从开源代码中拿来的部分,也请一般性地告诉我需要确认的许可证条件(不是法律建议也没关系)。代码里的密钥和个人信息我已经换成假值了。
[代码]

🧰 相关工具

可以当场检查、整理本讲涉及的 JSON、正则表达式和文本的工具。请不要粘贴真实的个人信息或密码。

参考
  • MDN Web Docs — Array.prototype.sort、JSON.parse、eval 参考文档(包括避免使用 eval 的说明)
  • OWASP — 关于输入验证和机密信息管理的一般指南
  • Open Source Initiative — 开源许可证(MIT、Apache-2.0、GPL 等)原文与一般说明

达成全部学习目标后,请点击完成。

💻 编程基础

  1. 1什么是程序:把事情拆成顺序、条件和循环的思维
  2. 2值、变量与数据类型:给值贴上名字标签
  3. 3条件语句:根据情况走不同的路
  4. 4循环 — 让同一件事准确地重复多次
  5. 5函数 — 拆成一个个有名字的小机器
  6. 6数组与对象 — 装载和处理数据
  7. 7字符串与文本处理——截取、查找、替换文字
  8. 8调试与读懂错误——把红字变成线索
  9. 9简单的自动化示例——用代码做表格计算和文本整理
  10. 10阅读并检验 AI 写的代码——测试、安全、许可证与个人信息
📚 推荐阅读
📊Using AI for Spreadsheets, and Checking the Formulas→ 💻AI Coding Assistants: Security and Licence Risks→ 💬Practising Conversation in a Foreign Language with AI→ ⚙️Finding Work Tasks Worth Automating with AI→
← AI时代基础学力课程