为了回馈我们的开发人员社区,我们检查了数千个项目的数据库,发现了JavaScript中最常见的10个错误。我们将告诉您是什么导致了这些错误,以及如何防止它们发生。如果你能避免陷入这些“陷阱”,你将成为一名更好的开发人员。
数据为王。我们收集并分析了前10个JavaScript错误。Rollbar收集每个项目的所有错误,并汇总每个错误发生的次数。我们根据“指纹”(rollbar使用的算法,详见https://rollbar.com/docs/grouping-algorithm/)对错误进行分组。基本上,如果第二个错误只是第一个错误的重复,我们会把两个错误分成同一个组。这将为用户提供一个很好的总结,而不是像日志文件中看到的那样,大量的转储会让人感到非常压抑。
我们关注最有可能影响您和您的用户的错误。为此,我们通过研究不同公司的项目集对错误进行排序。如果只看错误总数,客户数量多的项目产生的错误可能会压倒其他错误,导致实际收集的错误数据集与大多数读者无关。
以下是JavaScript错误前10名:
为了阅读方便,我们缩短了每个错误的描述。接下来,让我们深入每个错误,以确定是什么导致了它,以及如何避免创建它。
1.未捕获类型错误:无法读取属性
如果你是一个JavaScript开发人员,你可能会看到这个错误超过你敢承认的次数(LOL…)。当您读取未定义对象的属性或调用其方法时,此错误将出现在Chrome中。您可以在Chrome开发人员控制台中轻松测试(尝试)。
造成这种情况的原因有很多,但最常见的是呈现UI组件时状态初始化不当。
让我们看一个在实际应用程序中发生的例子:我们选择了React,但同样适用于Angular、Vue或任何其他框架。
课堂测验扩展了组件{ componentWillMount(){ axios . get('/the data ')。然后(RES={ this . setstate({ items : RES . data });});} render(){ return(ul { this . state . items . map(item=Li key={ item . id } { item . name }/Li)}/ul);}}这里有两件重要的事情要实现:
组件的状态(如this.state)以undefined开头。
当异步获取数据时,无论它是在构造函数componentWillMount还是componentDidMount中获取的,组件都会在加载数据之前至少呈现一次。首次呈现测验时,this.state.items未定义。这意味着ItemList将项定义为未定义,并且控制台中有一个错误——“不清楚typeerror :无法读取未定义的属性‘map’”。
这个很容易解决。最简单的方法:在构造函数中用合理的默认值初始化状态。
class Quiz扩展Component { //添加了this:构造函数(道具){ super(道具);//分配状态本身,以及项的默认值this . state={ items :[]};} ComponentWillMount(){ axios . get('/the data ')。然后(RES={ this . setstate({ items : RES . data });});} render(){ return(ul { this . state . items . map(item=Li key={ item . id } { item . name }/Li)}/ul);}}您的应用程序中的具体代码可能会有所不同,但我们希望我们已经为您提供了足够的线索来解决或避免您的应用程序中的这个问题。如果没有,请继续阅读,因为我们将在下面介绍更多相关错误的例子。
2.TypeError:“未定义”不是对象
这是在Safari中读取未定义对象的属性或调用方法时出现的错误。您可以在Safari开发人员控制台中轻松测试它。这与1中提到的Chrome的错误基本相同,但是Safari使用了不同的错误消息提示。
3.TypeError: null不是一个对象
这是在Safari中读取空对象的属性或调用空对象的方法时出现的错误。您可以在Safari开发人员控制台中轻松测试它。
有趣的是,null和undefined在JavaScript中是不同的,这就是为什么我们会看到两种不同的错误消息。Undefined通常是一个尚未赋值的变量,null表示该值为空。若要验证它们是否相等,请尝试使用严格相等运算符===:
在一个真实的例子中,可能发生这种错误的一种情况是,在加载元素之前,您试图使用JavaScript中的元素。因为对于空白对象引用,DOM API返回null。
任何执行和处理DOM元素的JS代码都应该在创建DOM元素之后执行。JS代码按照HTML中的规定从上到下进行解释。因此,如果DOM元素前面有一个标记,那么当浏览器解析HTML页面时,脚本标记中的JS代码将被执行。如果在加载脚本之前没有创建DOM元素,就会出现这个错误。
在这个例子中,我们可以通过添加一个事件监听器来解决这个问题,它会在页面准备好的时候通知我们。一旦addEventListener被触发,init()方法就可以使用DOM元素。
脚本函数init(){ var my button=document . getelementbyid(' my button ');var myTextfield=document . getelementbyid(' myTextfield ');mybutton . onclick=function(){ var userName=mytextfield . value;} } document . addeventlistener(' readystatechange ',function(){ if(document . readystate===' complete '){ init();} });/scriptform输入类型='text' id='myTextfield '占位符='键入您的姓名'/输入类型='button' id='myButton '值=' Go '/form 4。(未知):脚本错误
当浏览器的跨域策略限制了未捕获的JavaScript错误(由window.onerror处理程序引发的错误,而不是在try-catch中捕获的错误)时,就会发生这种脚本错误。例如,如果您将JavaScript代码托管在CDN上,任何未捕获的错误都将被报告为“脚本错误”,而不是包含有用的堆栈信息。这是一种浏览器安全措施,旨在防止数据跨域传输,否则将不允许通信。
要获得真正的错误消息,请执行以下操作:
1.发送“访问控制-允许-来源”标头
将访问控制允许源头设置为*意味着可以从任何域正确访问资源。如有必要,您可以用您的域替换该域:例如,访问控制-允许-来源:www.example.com。然而,处理多个域将变得棘手。如果使用CDN,可能会出现更多的缓存问题,这会让你觉得这种努力不值得。在这里看到更多。
以下是如何在各种环境中设置此头文件的一些示例:
街头流氓
在JavaScript文件所在的文件夹中,创建一个包含以下内容的. htaccess文件:
标题添加访问控制允许来源' * '
Nginx
将add_header指令添加到提供JavaScript文件的位置块:
位置~ ^/assets/{ add_header访问控制-允许-原点*;}
位置~ ^/assets/{ add_header访问控制-允许-原点*;}HAProxy
将以下内容添加到为JavaScript文件提供资源服务的后端:
rspadd访问控制-允许-原始: *
rspadd访问控制-允许-原始: *
2.在脚本中设置crossorigin='匿名'
在您的HTML代码中,对于您为其设置了访问控制允许源头的每个脚本,在脚本标签上设置crossorigin=“匿名”。在将crossorigin属性添加到脚本标记之前,请确保验证上述标头是否正确发送。在火狐中,如果crossorigin属性存在,但是Access-Control-Allow-Origin头不存在,脚本将不会被执行。
5.类型错误:对象不支持属性
当您调用未定义的方法时,这是IE中的一个错误。可以在IE开发者控制台进行测试。
这相当于Chrome中的“type error:“undefined”不是函数”错误。是的,对于同一个逻辑错误,不同的浏览器可能会有不同的错误消息。
对于使用JavaScript命名空间的Web应用程序,这是IE l浏览器的常见问题。在这种情况下,99.9%的原因是IE无法将当前命名空间中的方法绑定到这个关键字。例如,如果您有一个命名空间Rollbar,并且在JS中有一个方法isAwesome。通常,如果您在Rollbar命名空间中,可以使用以下语法调用isAwesome方法:
this . isawesome();
Chrome、Firefox和Opera将采用这种语法。另一方面,IE不会。因此,使用JS命名空间时最安全的选择是始终以实际命名空间作为前缀。
roll bar . isawesome();
6.TypeError:“未定义”不是函数
这是在Chrome中调用未定义函数时产生的错误。可以在Chrome开发者控制台和Mozilla Firefox开发者控制台进行测试。
随着过去几年JavaScript编码技术和设计模式的日益复杂,回调和闭包中的自引用范围也相应增加,这是造成这种/那种混淆的一个非常常见的来源。
考虑以下代码片段:
function TestFuncTion(){ this . ClearLocalStorage();this . timer=setTimeout(function(){ this . clearboard();//这是什么?}, 0);};执行上述代码将导致以下错误:“不明确的typeerror: undefined不是函数”。之所以会出现上述错误,是因为在调用setTimeout()时,实际上调用的是window.setTimeout()。因此,传递给setTimeout()的匿名函数是在窗口对象的上下文中定义的,它没有clearBoard()方法。
一个传统的、旧的浏览器兼容的解决方案是简单地将你的this保存在一个变量中,这个变量可以被闭包继承。例如:
function TestFuncTion(){ this . ClearLocalStorage();var self=这个;//保存对“this”的引用,而它仍然是this!this . timer=setTimeout(function(){ self . clearboard();}, 0);};或者,在较新的浏览器中,您可以使用bind()方法传递适当的引用:
function TestFuncTion(){ this . ClearLocalStorage();this . timer=setTimeout(this . reset . bind(this),0);//绑定到“this”};function TestFuncTion(){ this . ClearBoard();//回到右边‘这个’的上下文中!};7.未捕获范围错误:最大调用堆栈
这是Chrome在某些情况下会犯的错误。一种是调用不终止的递归函数。您可以在Chrome开发人员控制台中测试它。
此外,如果将值传递给超出范围的函数,可能会发生这种情况。许多函数只接受特定输入值范围内的数字。例如:Number.toExponential(数字)和Number.toFixed(数字)接受0到20之间的数字,Number.toPrecision(数字)接受1到21之间的数字。
var a=新数组(4294967295);//OKvar b=新数组(-1);//range error var num=2.555555;document . writeln(num . to exponential(4));//okdocument . writeln(num . toexponential(-2));//范围错误!num=2.9999document . writeln(num . tofixed(2));//okdocument . writeln(num . tofixed(25));//范围错误!num=2.3456document . writeln(num . Toprecision(1));//okdocument . writeln(num . Toprecision(22));//范围错误!8.类型错误:无法读取属性'长度'
这是Chrome中的一个错误,因为读取的是未定义变量的长度属性。您可以在Chrome开发人员控制台中测试它。
您通常会在数组中找到定义的长度,但是如果数组没有初始化或者变量名隐藏在另一个上下文中,您可能会遇到此错误。让我们用下面的例子来理解这个错误。
var testArray=[' Test '];函数TestFuncTion(TestArray){ for(var I=0;我测试长度;I){ console . log(TeStarray[I]);} } TestFuncTion();当您用参数声明函数时,这些参数会成为函数范围内的局部参数。这意味着,即使在函数外部有一个名为testArray的变量,函数中同名的参数也将被视为局部参数。
你有两种方法来解决你的问题:
1.删除函数声明语句中的参数(实际上,您希望访问在函数外部声明的变量,因此不需要函数的参数):
var testArray=[' Test '];/*预条件:在函数外定义了testArray */function testFunction(/*无参数*/){ for(var I=0;我测试长度;I){ console . log(TeStarray[I]);} } TestFuncTion();2.用声明的数组调用函数:
var testArray=[' Test '];函数TestFuncTion(TestArray){ for(var I=0;我测试长度;I){ console . log(TeStarray[I]);} } TestFunction(TestArray);9.未捕获类型错误:无法设置属性
当我们试图访问一个未定义的变量时,它总是返回undefined,我们不能获取或设置任何未定义的属性。在这种情况下,应用程序将抛出“不清楚的typeerror :无法设置属性”。
例如,在Chrome浏览器中:
如果测试对象不存在,错误将抛出“unhighttypeerror unhighttypeerror 3360无法设置属性”。
10.未定义引用错误:事件
当您试图访问未定义的变量或超出当前范围的变量时,会引发此错误。可以在Chrome浏览器中轻松测试。
如果在使用事件处理系统时遇到此错误,请确保使用传入的事件对象作为参数。像IE这样的旧浏览器提供了一个全局变量事件,但并不是所有的浏览器都支持它。像jQuery这样的库试图规范这种行为。但是,最好使用传递给事件处理程序的函数。
function my function(event){ event=event . what | | event . key code;if(event . key code===13){ alert(event . key code);}}摘要
以上是边肖介绍的10个最常见的Javascript错误。我希望他们会帮助你。如果你有任何问题,请给我留言,边肖会及时回复你。非常感谢您对我们网站的支持!