如果在一个函数内部发生了错误,它自身没有捕获,错误就会被抛到外层调用函数,如果外层函数也没有捕获,该错误会一直沿着函数调用链向上抛出,直到被JavaScript引擎捕获,代码终止执行
function getLength(s)
{ return s.length; };//返回长度
function printLength()
{ console.log(getLength('abc')); console.log(getLength(null));
}
printLength();
3
TypeError: s is null//现在控制台返回了abc的长度,然后报错
因为每个函数都没有错误处理,所以最后直接由javascript捕获进而报错
同时,我们不必在每一个函数内部捕获错误,只需要在合适的地方来个统一捕获,一网打尽,至于在哪些地方捕获错误比较合适,需要视情况而定
function main(s)
{
console.log('开始main()');
try {foo(s); }
catch (e) { alert('出错了:' + e); }
console.log('结束main()');
};
function foo(s)
{
console.log('开始 foo()');
bar(s);
console.log('结束 foo()');
};
function bar(s)
{
console.log('开始bar()');
console.log('length = ' + s.length);
console.log('结束 bar()'); };
main(null);
开始main()
开始 foo()
开始bar()
出错了:TypeError: s is null
这里是在最上层函数里加了错误处理,main调用foo,foo调用bar
当bar()函数传入参数null时,代码会报错,错误会向上抛给调用方foo()函数,foo()函数没有try …catch语句,所以 错误继续向上抛给调用方main()函数,main()函数有try …catch语句,所以错误最终在main()函数被处理了
JavaScript引擎是一个事件驱动的执行引擎,代码总是以单线程执行,而回调函数的执行需要等到下一个满足条件的事件出现后,才会被执行
setTimeout()是个很特别的函数,其他的函数都是要按着次序依次执行,而这个函数内的回调函数会在设置了若干毫秒后执行,并且一旦设置号时间后,会继续往下执行,不会等着回调函数
所以在含有setTimeout代码里可能看到setTImeout后面的代码可能会先执行
function printTime()
{ console.log('It is time!'); }
setTimeout(printTime, 1000);
console.log('done');
done
It is time!
跟我说的一样吧,先打印了done,再过1秒打印it is time
如果printTime()函数内部发生了错误,我们试图用try包裹setTimeout()是无效的,不信来尝试一下
function printTime()
{ throw new Error(); }
try {
setTimeout(printTime, 1000);
console.log('done');
}
catch (e) { alert('error'); }
//并没有alert
原因就在于调用setTimeout()函数时,传入的printTime函数并未立刻执行,紧接着,JavaScript引擎会继续执行console.log('done');
语句,而此时并没有错误发生,直到1秒钟后,执行printTime函数时才发生错误,但此时printTime函数内部并不能捕获错误,外层代码也无法捕获
所以,涉及到异步代码,无法在调用时捕获,原因就是在捕获的当时,回调函数并未执行
类似的,当我们处理一个事件时,在绑定事件的代码处,无法捕获事件处理函数的错误
<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-3.5.1.js"></script>
</head>
<body>
<form>
<input id="x"> + <input id="y">
<button id="calc" type="button">计算</button>
</form>
<script>
function caculate()//定义了一个算加法的函数,并且如果输入不合法会抛出错误
{
var x = parseFloat($('#x').val());//把输入的x值转化为浮点数
var y = parseFloat($('#y').val());
var r;
if (isNaN(x) || isNaN(y))//如果出现字母,则oarseFload会返回Nan
{ throw new Error('输入有误'); }
else {r = x + y; alert('计算结果:' + r);}
}
var btn = $('#calc'); //绑定到按钮上
try
{
btn.on('click', caculate);//一旦提交就调用
}
catch (e) { alert('输入有误!'); }
</script>
</body>
</html>
上面这个代码可以正常执行计算,但如果遇到不合法输入时,并不会alert
因为当我们进入try语句后,一旦监听到click事件,就会去调用caculate函数,btn.on这句话就算执行完了,就开始捕捉error,但这个时候其实caculate还在运行,还没有throw error,所以catch也结束了,当真正error被抛过来之后,又没有外层捕捉函数了,所以最后javascript捕捉到了错误,报错在了控制台
下面是可以正确处理错误的代码
<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-3.5.1.js"></script>
</head>
<body>
<form>
<input id="x"> + <input id="y">
<button id="calc" type="button">计算</button>
</form>
<script>
var btn = $('#calc');
btn.on('click', caculate);
function caculate(){
try {
var x = parseFloat($('#x').val());
var y = parseFloat($('#y').val());
var r;
if (isNaN(x) || isNaN(y)){ throw new Error('输入有误'); }
else{r = x + y; alert('计算结果:' + r); }
}
catch (e)
{ alert('输入有误!'); }
};
</script>
</body>
</html>
直接把计算过程放在try内部,就毫无问题啦