JS常见错误和解决方法集锦
hello大家好,很多同学使用webfunny前端监控系统也有一段时间了,发现在js错误统计功能中遇到了一些问题,不知道该如何下手解决。前端监控平台只是一个辅助工具(并非神器),正常情况下它们只能搜集问题,并以更好的方式展示给我们看,并不能够直接解决问题,所以还是需要我们根据错误信息来处理错误。今天我将为大家总结一下前端开发中常见的问题,以及解决方案。
第一类、运行时代码错误
运行时错误,会有很多种类型,分别为:
1. SyntaxError:语法错误;
这个一般是新手开发者容易犯得的错误,比如变量名的命名不规范,给关键字进行赋值,花括号没有成对儿出现等等。这些错误很容易在开发的时候就被发现,
常见的错误信息如: SyntaxError: Invalid or unexpected token;Uncaught SyntaxError: Unexpected token =;Uncaught SyntaxError: Unexpected number;常见代码如下:
// 以数字开头的变量名 var 1a // Uncaught SyntaxError: Invalid or unexpected token // 给关键字赋值 function = 5 // Uncaught SyntaxError: Unexpected token = // 没有写完的花括号 if (true) { console.log(1) else { console.error(1) }
2. Uncaught ReferenceError:引用错误
引用一个不存在的变量时发生的错误。将一个值分配给无法分配的对象,比如对函数的运行结果或者函数赋值。
常见错误信息如:Uncaught ReferenceError: xxx is not defined;Uncaught ReferenceError: Invalid left-hand side in assignment;
//引用了不存在的变量
a() // Uncaught ReferenceError: a is not defined
console.log(b) // Uncaught ReferenceError: b is not defined
//给一个无法被赋值的对象赋值
console.log("abc") = 1 // Uncaught ReferenceError: Invalid left-hand side in assignment
3. RangeError:范围错误
RangeError是当一个值超出有效范围时发生的错误。主要的有几种情况,第一是数组长度为负数,第二是Number对象的方法参数超出范围,以及函数堆栈超过最大值。还有一种情况是当你调用一个不会终止的递归函数时。
常见错误信息如:Uncaught RangeError: Maximum Call Stack;Uncaught RangeError: Invalid array length;
// 数组长度为负数 [].length = -5 // Uncaught RangeError: Invalid array length // Number对象的方法参数超出范围 var num = new Number(12.34) console.log(num.toFixed(-1)) // Uncaught RangeError: toFixed() digits argument must be between 0 and 20 at Number.toFixed // 说明: toFixed方法的作用是将数字四舍五入为指定小数位数的数字,参数是小数点后的位数,范围为0-20.
4. TypeError类型错误
变量或参数不是预期类型时发生的错误。比如使用new字符串、布尔值等原始类型和调用对象不存在的方法就会抛出这种错误,因为new命令的参数应该是一个构造函数。
常见错误类型如:Uncaught TypeError: Cannot Read Property;TypeError: ‘undefined’ Is Not an Object (evaluating...);TypeError: Null Is Not an Object (evaluating...);TypeError: ‘undefined’ Is Not a Function;TypeError: Cannot Read Property ‘length’;Uncaught TypeError: Cannot Set Property;
// 调用不存在的方法 123() // Uncaught TypeError: 123 is not a function var o = {} o.run() // Uncaught TypeError: o.run is not a function // new关键字后接基本类型 var p = new 456 // Uncaught TypeError: 456 is not a constructor
5. URIError,URL错误
主要是相关函数的参数不正确。
创建错误信息如:Uncaught URIError: URI malformed at decodeURI;
decodeURI("%") // Uncaught URIError: URI malformed at decodeURI
总结&解决方案:运行时错误是js代码中最常见的,原则上说它是js代码中最严重的错误,因为它会阻断js代码的运行,如果你不去捕获并处理它,接下来的js代码都不会再执行了。它也是比较容易定位和解决的一种js错误,因为它有非常详细的错误堆栈,可以精确到行列号。借此,我们就可以定位到js错误发生的具体位置。通过sourceMap映射文件,我们也可直接定位到它的源码位置。也可以跳转到行为轨迹上,分析它发生环境和时机。如图:
第二类、自定义错误
面对复杂的业务需求,有时候我们不得不定义一些错误,来帮助我们排查问题。
第一种,比如接口返回了异常结果,但是接口本身是一个正常的请求,这个时候我们就需要把接口返回的状态码以错误的形式进行统计,但是这类错误跟运行时代码错误有着本质的区别,所以并不能以错误的形式进行上报,我们可以将其列为自定义错误。
第二种,由我们引入的第三方库发出的报错。第三方为了保证业务的运行,一般不会抛出错误(throw error),因为throw error一定会阻断业务逻辑,所以他们一般都会采用:先捕获异常,然后采用 console.error的形式打印出报错信息或者警告信息,例如,jquery、vue.js、react.js等框架都会出现一些常见的警告信息。
第三种,由我们自己定义error,通常是打印一句error message,如:console.error("我报错了")
总结:所有通过console.error打印出来的错误,我们都将其定义为【自定义错误】。因为利用console.error打印message的方式,必然是我们已经知道了处理方式,即使第三方库也是如此。自定义错误没有错误堆栈,无法定位到具体的代码位置,但是它可以补全我们排查的链路。如图:
第三类、(unknown): Script Error
当未捕获的 JavaScript 错误违背跨域原则时,就会发生脚本错误。例如,如果将 JavaScript 代码托管在 CDN 上,则任何未被捕获的错误(通过 window.onerror 处理程序发出的错误,而不是 try-catch 中捕获到的错误)将仅报告为“脚本错误”。这是浏览器的一种安全机制,主要用于防止跨域传递数据的情况出现。
通常我们可以不用关注或者解决这种错误,因为是第三方的错误,所以我们自己无法解决。 如果到了不得不解决的时候,我们可以提供详细的行为记录和发生的时机,然后让第三方来解决。
还有一种方式,将第三方的js文件的跨域头Access-Control-Allow-Origin 设置为 *, 表示可以从任何域正确访问资源(需要第三方同意)。或者感觉将第三方文件直接放到自己的域名下,这样就可以定位到错误了。
第四类、其他类错误
1. 未处理的异常:unhandledrejection
当我们使用Promise的时候,
被reject且没有reject处理器的时候,会触发 unhandledrejection
事件;这个时候,就会报一个错误:unhanled rejection;没有堆栈信息,只能依靠行为轨迹来定位错误发生的时机。
2. 内存泄漏
js的内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。通俗点就是指由于疏忽或者错误造成程序未能释放已经不再使用的内存,不再用到的内存却没有及时释放,从而造成内存上的浪费。
如何避免:在局部作用域中,等函数执行完毕,变量就没有存在的必要了,垃圾回收机制很亏地做出判断并且回收,但是对于全局变量,很难判断什么时候不用这些变量,无法正常回收;所以,尽量少使用全局变量。在使用闭包的时候,就会造成严重的内存泄漏,因为闭包中的局部变量,会一直保存在内存中。
第五类、手动抛出异常
我们在书写逻辑的时候,比如作为第三方库的时候,对于严重的错误,我们无法进行处理,可以直接将错误抛出来,警告使用者。但是throw error会阻断程序运行,请谨慎使用。
throw new Error("出错了!");
throw new RangeError("出错了,变量超出有效范围!");
throw new TypeError("出错了,变量类型无效!");
好了,以上就是我总结常见错误问题,我也会持续补充。