调试技巧

在开发过程中,我们可能会遇到报错,脚本无法正常运行,或者运行结果和预期不一致。这时候我们需要进行调试,找出问题所在,并修复问题。本章将介绍一些调试的基本技巧。

删除可能出错的脚本

当遇到报错时,首先要找到出错的位置。尝试删除一行或多行可能出错的代码,然后重新执行程序,观察是否依然有报错。通过这种方法逐步缩小错误代码的范围,直到定位到具体出错的代码。
将可能出错的代码行前增加 //,表示这段代码为注释,不会被执行。
例如:
// 物品.金钱 = 物品.金钱 * 2

打印变量

当我们发现程序中某个属性值或临时变量不符合预期时,可以通过使用提示打印变量,跟踪变量在执行过程中的变化,从而确定是否是中间过程出现了问题。
例如:
属性.签到次数打印出来,帮助我们观察中间过程中签到次数的变化,判断是否存在问题:
for (var i = 0; i < 10; i = i + 2) {
  属性.签到次数 = 属性.签到次数 + 1;
  提示(属性.签到次数); // 发送提示,内容是当前签到次数的值
}
读取数据库的值,然后打印出来,从而调试数据库是否按照预期进行工作:
数据.(['成员', 玩家.id, '金钱'], 100)

var 金钱数值 = 数据.(['成员', 玩家.id, '金钱'])
提示(金钱数值); // 发送提示,内容是数据库中的值
直接打印一个文本,判断该条件分支是否被执行到:
if (xxx) {
    xxxx;
    if (xxx) {
        xxxx;
        return;
    } else {
        提示('此处被执行了')  // 发送提示,说明这里被执行过
    }
}

逐步执行

逐步执行代码可以帮助我们找到出错的具体位置。一个一个地执行可能出错的代码段,检查每一段代码执行后的对变量和属性的影响。一旦发现问题,就修复它,确保每一步都符合预期。
步骤1;
// 步骤2;
// 步骤3;
// 步骤1;
步骤2;
// 步骤3;
// 步骤1;
// 步骤2;
步骤3;

常见情况

如果进行了简单的调试,代码却依然没有按照预期工作时,往往是以下几种可能:

代码功能和预期不符

某段代码的作用和想象的不一样。
例如,下面这段代码错误的判断了签到次数是否等于1、2、3:
if (属性.签到次数 === 1 || 2 || 3) {
    // 相当于 属性.签到次数 === 1 || true || true,条件始终成立
    // 因此,if 里面的代码将始终被执行
}
遇到这种情况,应当逐步执行代码,检查代码执行过程中的临时变量,找到开始不符合预期的位置。

输入值超出预期

来自属性或数据库的值可能超出预期。例如,玩家输入的数字有可能是负数,数据库的值有可能是undefined。
例如,下面这段代码读取玩家所在公会的人数,并增加人数。
var 人数 = 数据.(['公会', 属性.公会名称, '人数']); // 公会数据可能不存在,读取的数值为 undefined
人数++; // undefined + 1 会变成 NaN
数据.(['公会', 属性.公会名称, '人数'], 人数);  // 最终写回数据库的值就变成了 NaN,导致报错
遇到这种情况,应当使用提示检查每条命令所使用的值是否符合预期。

其它因素

  • 某段代码没有被执行(检查 if 条件)
  • 某段代码意外被执行(检查 if 条件、延迟执行和活跃动作)
  • 错打了某个符号,导致语义发生变化(处理办法与“代码功能和预期不符”类似)

开发时的注意事项

如果我们想开发一个复杂的功能时,需要理清整体流程,思考代码会怎么运行,有哪些步骤和分支,要存储哪些数据,用户要怎么操作。
如果不确定功能是否能按照预期想法工作,可以将其拆分成一些简单的逻辑进行开发。每开发完一个简单功能,验证它是否可以按照预期工作。
当不熟练如何开发时,尽量避免根据“直觉”和“感觉”进行功能设计和开发,而是使用纸笔对运行过程进行演算,搞明白功能细节如何运作,数据是如何发生变化的。

坚定信念

在调试代码时,需要不断缩小错误范围,增加“我确定这段代码是正确的”的比例,避免因盲目尝试而原地踏步。
代码始终会按照命令进行执行,如果没有按照预期工作,那么肯定是由某个确定的因素导致的。要相信,只要思路正确,代码一定可以被修复并成功运行。