声明:前面是需求的来源和详解,想看最后效果的可以直接翻到最后面
在我们开发项目的过程中,有些地方可能理论上是没有问题的,但是为了以防万一,还是加了一个判断用来预防。
例如:
function f1() {
return parseInt(Math.random() * 2 + 1);
}
function f2() {
let x = f1();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
console.log('出bug了');
}
}
f2();
上述代码中,f1函数在一些情况下会返回数字1一些情况下会返回或数字2(当然上面的代码就只是用一个随机数模拟一下这个情景),f2函数知道f1函数只会返回1或者2,于是用if-else判断f1函数返回的是1还是2,并做一些处理,如果二者都不是,则打印一下出bug了,这样在调试的时候我们看到了控制台打印了’出bug了’之后就会知道这里出了问题。
现在假设我们在后来某一次改bug或者加需求的时候改了一下f1函数:
function f1() {
return parseInt(Math.random() * 3 + 1);
}
现在f1函数的返回值就不止是1或2了,也就是f1函数被我们改的逻辑错了,但是我们并没有意识到,于是我们在调试的时候,偶然的发现控制台里打印了’出bug了’
而且还贴心的的告诉了我们这个’出bug了’是哪行代码打印的,然后看了那行代码之后,我们就知道我们在之前某次的改动中把f1函数给改出bug了,于是我们就会去修复这个bug。
但是上面这种情况是我们运气好看到了控制台打印了这个,如果我们调试的时候没在看控制台,而是在观察其它的东西(比如在看Network或者是页面中的变化啥的),这样的话我们可能就不能第一时间知道这个地方出bug了,而是好久之后才发现,这样就会降低我们调试的效率。
所以我们为了能够第一时间知道哪里有bug,可以把console.log
换成alert
。
function f2() {
let x = f1();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
alert('出bug了');
}
}
这样如果f1函数出现了问题的话,会直接在页面上弹一下,比较的显眼,就可以第一时间发现问题从而去解决问题。
这样我们就可以第一时间发现问题了,于是我们会去代码里找是哪行代码alert
的这个,从而去修复这个bug
但是新的问题出现了,如果这种不确定的bug比较多的话
function f1() {
return parseInt(Math.random() * 3 + 1);
}
function f2() {
let x = f1();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
alert('出bug了');
}
}
f2();
function f3() {
return parseInt(Math.random() * 3 + 1);
}
function f4() {
let x = f3();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
alert('出bug了');
}
}
f4();
function f5() {
return parseInt(Math.random() * 3 + 1);
}
function f6() {
let x = f5();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
alert('出bug了');
}
}
f6();
这样当页面alert
出bug了之后我们就不知道到底是哪里的代码alert
的了
于是我们可能这样做
function f1() {
return parseInt(Math.random() * 3 + 1);
}
function f2() {
let x = f1();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
alert('第12行代码处出bug了');
}
}
f2();
function f3() {
return parseInt(Math.random() * 3 + 1);
}
function f4() {
let x = f3();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
alert('第27行代码处出bug了');
}
}
f4();
function f5() {
return parseInt(Math.random() * 3 + 1);
}
function f6() {
let x = f5();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
alert('第42行代码处出bug了');
}
}
f6();
这样再alert
的时候我们就知道是哪行alert
的了
不过这样还是有一个问题,如果我们在这写代码上面的代码做了一些修改的话
function f1() {
return parseInt(Math.random() * 3 + 1);
}
function f2() {
let x = f1();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
alert('第12行代码处出bug了');
}
}
f2();
// 这里加了一点代码 --------------
let a = 1;
// ----------------
function f3() {
return parseInt(Math.random() * 3 + 1);
}
function f4() {
let x = f3();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
alert('第27行代码处出bug了');
}
}
f4();
// 这里加了亿点代码 --------------
let sum = 0;
for (let i = 1; i <= 100; i++) {
sum += i;
}
console.log(sum);
const html = document.documentElement;
const body = html.children[1];
body.style.backgroundColor = 'pink';
let div = document.createElement('div');
div.innerText = 'hello bug';
body.appendChild(div);
// ----------------
function f5() {
return parseInt(Math.random() * 3 + 1);
}
function f6() {
let x = f5();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
alert('第42行代码处出bug了');
}
}
f6();
这些alert
语句所在的行数就会发生改变,如果改动的不多的话还好,最后也会在附近
但是改动比较多的话就不太好找了,这样就不利于我们去找bug
既然这样,那么为了防止alert
窜行,我们可以在程序里先获取一下当前alert代码所在的行号,然后再alert出来
于是我去在网上搜了一波,但是也没搜到js能获取行号的内置函数
既然没有自带的,那我们就自己写一个吧哈哈哈
function getRow() {
return // 当前行数
}
假设我们如果写好了一个这样功能的函数,那么之前的代码就可以改成这样,就可以解决我们的问题了:
function f1() {
return parseInt(Math.random() * 3 + 1);
}
function f2() {
let x = f1();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
alert('第1' + getRow() + '行代码处出bug了');
}
}
f2();
// 这里加了一点代码 --------------
let a = 1;
// ----------------
function f3() {
return parseInt(Math.random() * 3 + 1);
}
function f4() {
let x = f3();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
alert('第' + getRow() + '行代码处出bug了');
}
}
f4();
// 这里加了亿点代码 --------------
let sum = 0;
for (let i = 1; i <= 100; i++) {
sum += i;
}
console.log(sum);
const html = document.documentElement;
const body = html.children[1];
body.style.backgroundColor = 'pink';
let div = document.createElement('div');
div.innerText = 'hello bug';
body.appendChild(div);
// ----------------
function f5() {
return parseInt(Math.random() * 3 + 1);
}
function f6() {
let x = f5();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
alert('第' + getRow() + '行代码处出bug了');
}
}
f6();
那么本文的核心来了,要如何获取行号呢
首先,先来回忆一下,我们都在什么地方见到过代码行号
首先,上文中提到的console.log()
在将内容打印到控制台的时候,在右侧会显示是哪行打印的,这里就出现了一个行号,但是console.log()
这个东西它的内部比较高深,目前已咱这个水平也研究不明白
那么就需要再想想哪里还出现过行号
还记不记得,每次当代码报错的时候,都会显示是哪行代码报的错,这里也出现了行号!
throw new Error('错误原因');
既然这个异常对象里面有报错的代码的行数,那我们就好好研究一下这个Error
吧
console.log(new Error('错误原因'));
如果我们直接将这个异常对象打印出来的话,是这样的
既然这样,我们就可以分割一下字符串,从而获取到这个行号1,记得要先转化成字符串,这里我是通过冒号分割的
console.log((new Error('错误原因') + '').split(':'));
emmm看样子好像不太行,异常对象转化成字符串之后和在console.log
里打印的东西不太一样,可能是以为内部有什么转换机制吧,不太清楚
那么……就要另寻他路
我们先用浏览器看看异常对象里都有啥吧
emmm根据观察
首先constructor
是构造函数,应该没啥用
然后message
应该是异常的信息,就是我们new的时候传进去的那个
name
应该是这个异常的类型的名字,也用处不大
toString()
就不用说了,刚刚上面转化成字符串的时候内部调用的就是这个函数
那么就剩一个stack
了
那么我们看看这是啥
console.log(new Error().stack);
console.log(typeof new Error().stack);
很好,这里面有行号,而且它还是个字符串,这不就正是我们需要的东西吗
那么我们就来利用一下它
let e = new Error();
console.log(e.stack);
console.log(e.stack.split(':'));
let e = new Error();
// console.log(e.stack);
console.log(e.stack.split(':')[3]);
function getRow() {
let e = new Error();
// console.log(e.stack);
return e.stack.split(':')[3];
}
console.log(getRow());
好像不太对,我们需要的应该是调用getRow
函数的那一行的行号也就是6,但是返回的是创建异常对象的行号2
不难发现,异常对象中还会记录函数的调用的行号
那么我们再做个实验
function getRow() {
let e = new Error();
console.log(e.stack);
console.log(e.stack.split(':'));
// return e.stack.split(':')[3];
}
function f1() {
console.log(getRow());
}
function f2() {
f1();
}
function f3() {
f2();
}
f3();
f2();
console.log(getRow());
异常对象会把层层调用的函数的行号都记录下来,我们需要的是调用getRow
函数的行号,也就是第二层的调用,也就是分割后的数组的索引为7的那个元素
function getRow() {
let e = new Error();
return e.stack.split(':')[7];
}
function f1() {
console.log(getRow());
}
function f2() {
f1();
}
function f3() {
f2();
}
f3();
console.log(getRow());
现在我们的getRow
函数就可以成功且准确的获取到代码的行号了!!!
考虑到以后或许可能还会用需要用这个行号计算(虽然这个几乎不可能,应该不会用需要用行号计算的需求吧,我觉得我这个获取行号的需求已经挺奇葩的了哈哈哈),所以返回的时候也可以转化成数值型
那么最终的效果就是这样啦:
/**
* 函数功能:获取代码行号
*
* @returns 返回函数调用处的代码行号
* @author 60rzvvbj
*/
function getRow() {
return parseInt(new Error().stack.split(':')[7]);
}
// 用之前的代码测试一下
function f1() {
return parseInt(Math.random() * 3 + 1);
}
function f2() {
let x = f1();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
alert('第' + getRow() + '行代码处出bug了');
}
}
f2();
// 这里加了一点代码 --------------
let a = 1;
// ----------------
function f3() {
return parseInt(Math.random() * 3 + 1);
}
function f4() {
let x = f3();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
alert('第' + getRow() + '行代码处出bug了');
}
}
f4();
// 这里加了亿点代码 --------------
let sum = 0;
for (let i = 1; i <= 100; i++) {
sum += i;
}
console.log(sum);
const html = document.documentElement;
const body = html.children[1];
body.style.backgroundColor = 'pink';
let div = document.createElement('div');
div.innerText = 'hello bug';
body.appendChild(div);
// ----------------
function f5() {
return parseInt(Math.random() * 3 + 1);
}
function f6() {
let x = f5();
if (x == 1) {
// 如果是1,则进行一些操作
} else if (x == 2) {
// 如果是2,则进行一些操作
} else {
// 如果既不是1也不是2,则说明f1函数的逻辑出现了错误
alert('第' + getRow() + '行代码处出bug了');
}
}
f6();