译者按: null/undefined引起的错误在10大错误中比例很高。而它们极可能致使严重问题,因此要重视起来。javascript
为了保证可读性,本文采用意译而非直译。另外,本文版权归原做者全部,翻译仅用于学习。css
为了回馈拥护咱们的开发者,咱们将全部项目数据分析了一下,总结出10大JavaScript错误。咱们会详细解释错误的缘由以及如何预防再次发生。若是你学会了避开这些坑,那么你将会是一个更加出色的开发者。html
现在数据为王,咱们聚合了大量BUG数据,并对它们进行分析,列出了排名前十的JavaScript错误。Rollbar收集每个项目全部的错误,并统计它们发生的次数。咱们将相同的错误聚合起来。若是同一个错误出现不少次的话,这样就能够避免像日志同样很是多,让人无从下手。html5
咱们将统计同一个错误在多少个项目中出现,并以此来排序。以下所示:java
为了方便阅读,每一条错误咱们将后面的内容作了适当省略。接下来咱们详细介绍每个错误。ios
若是你是一个JavaScript开发者,这种错误大概你已经见怪不怪了。在Chrome下,当你从一个不存在的对象(undefined)获取属性或则进行函数调用,就会报这样的错。你能够在Chrome浏览器控制台测试:git
有不少种缘由能够致使这种状况的出现,一个常见的状况是在渲染UI部件的时候,没有正确地初始化状态(state)。咱们来看一个真实的例子。在这里我选用React,不过内在的原理一样适用于Angular、Vue或则其它框架。axios
class Quiz extends Component { componentWillMount() { axios.get('/thedata').then(res => { this.setState({items: res.data}); }); } render() { return ( <ul> {this.state.items.map(item => <li key={item.id}>{item.name}</li> )} </ul> ); } }
这里有两个关键点:segmentfault
this.state
)没有初始化,值为undefined
。componentWillMount
或则componentDidMount
是否获取数据无关。也就是说,当Quiz第一次渲染的时候,this.state.items
是未定义的。所以,会报错:"Uncaught TypeError: Cannot read property ‘map’ of undefined"
。这个bug很容易修复。最简单的方法:在构造函数中初始化state。后端
class Quiz extends Component { // Added this: constructor(props) { super(props); // Assign state itself, and a default value for items this.state = { items: [] }; } componentWillMount() { axios.get('/thedata').then(res => { this.setState({items: res.data}); }); } render() { return ( <ul> {this.state.items.map(item => <li key={item.id}>{item.name}</li> )} </ul> ); } }
也许在你的应用中会有点不同,不够但愿可以给你一些线索帮助你去修复或则避免这样的问题。若是没有,那么继续往下看吧,还有更多相关的例子等着你呢。
在Safari下,若是在一个未定义(undefined)的对象上读取属性或则调用函数,就会触发这样的错误。你能够在Safari控制台测试。这个错误根本上来讲和第一个在Chrome下的错误是同样的,只是错误的消息不一样。
备注:Fundebug早已机智地将这两种状况聚合为一个错误了,更加方便分析,欢迎各位老铁试用!
在Safari下,若是你尝试从null读取属性或则调用方法,就会报错。以下:
有趣的是,在JavaScript中,null和undefined是不一样的,因此咱们看到两个不一样的错误消息。Undefined指的是一个变量没有被赋值,而null指的是值为空。咱们能够用===
来判断:
一种现实中可能的状况就是:若是你尝试在一个DOM元素加载以前使用它。那么DOM API就会返回null。任何处理DOM元素的JS代码都应当在DOM加载完毕以后调用。JS代码是按照代码的顺序从上往下依次解释执行。若是在DOM元素前有脚本,那么在浏览器分析HTML页面的时候,JS代码也在执行了。若是JS代码执行的时候,DOM尚未建立好,那么你会遇到这个错误。
最经常使用的解法是使用事件监听,当DOM加载完毕以后,再触发JS代码的执行。
<script> function init() { var myButton = document.getElementById("myButton"); var myTextfield = document.getElementById("myTextfield"); myButton.onclick = function() { var userName = myTextfield.value; } } document.addEventListener('readystatechange', function() { if (document.readyState === "complete") { init(); } }); </script> <form> <input type="text" id="myTextfield" placeholder="Type your name" /> <input type="button" id="myButton" value="Go" /> </form>
来自网友的备注:
当未捕获的 JavaScript 错误(经过window.onerror处理程序引起的错误,而不是捕获在try-catch中)被浏览器的跨域策略限制时,会产生这类的脚本错误。 例如,若是您将您的 JavaScript 代码托管在 CDN 上,则任何未被捕获的错误将被报告为“脚本错误” 而不是包含有用的堆栈信息。这是一种浏览器安全措施,旨在防止跨域传递数据,不然将不容许进行通讯。
想要获取到真实详细的错误信息,你能够像这样作:
在header里添加 Access-Control-Allow-Origin 字段
在header(这应该是服务器返回的response header)字段里,把Access-Control-Allow-Origin设为,这样就表示来自任意的域名请求均可以正确地访问到服务器的资源。必要的话也能够指定具体的域名来代替星号,好比:Access-Control-Allow-Origin: www.example.com。可是配置的域名太多的话,处理起来会有点棘手,并且若是你在使用CDN的话还会出现缓存的问题,这样就有点费力不讨好了。更多参考这里。
下面举一些在各类环境下配置这个header的示例:
Apache
在JavaScript代码所在的文件夹目录下,新建一个.htaccess文件,内容以下:
Header add Access-Control-Allow-Origin "*"
Nginx
在JavaScript代码所在文件夹目录下面,添加add_header命令:
location ~ ^/assets/ { add_header Access-Control-Allow-Origin *; }```
HAProxy
在后端的JavaScript所在文件加入如下内容:
rspadd Access-Control-Allow-Origin:\ *
在IE中,若是调用未定义的方法就会发生这种错误。您能够在IE开发者控制台中进行测试。
至关于 Chrome 中的 “TypeError:”undefined“ is not a function” 错误。 对于相同的错误,不一样的浏览器具备不一样的错误消息。
在IE里使用JavaScript的命名空间时,就很容易碰到这个错误。发生这个错误十有八九是由于IE没法将当前命名空间里的方法绑定到this关键字上。例如,假设有个命名空间Rollbar,它有一个方法叫isAwesome()。在Rollbar命名空间中,能够直接使用this关键字来调用这个方法:
this.isAwesome();
在Chrome、Firefox和Opera中这样作都是没有问题的,但在IE中就不行。因此,最安全的作法是指定全命名空间:
Rollbar.isAwesome();
在Chrome下,调用一个未定义的函数时就会发生这个错误,能够在Chrome/Mozilla开发者控制台测试:
随着js代码的编码技巧和设计模式愈来愈复杂,在回调函数、闭包等各类做用域中this的指向的层级也随之增长,这就是js代码中this/that指向容易混淆的缘由。
好比下面这段代码:
function testFunction() { this.clearLocalStorage(); this.timer = setTimeout(function() { this.clearBoard(); // 这里的”this"是指什么? }, 0); };
执行上面的代码会报错:“Uncaught TypeError: undefined is not a function”。由于在调用setTimeout()方法时,其实是在调用window.setTimeout()。传给setTimeout()的匿名函数的this其实是window,而window并不包含clearBoard()方法。
一个最简单的、能兼容旧版本浏览器的方法,就是先把this指向赋值给一个变量self,而后在闭包里直接引用这个self变量。例如:
function testFunction () { this.clearLocalStorage(); var self = this; // 将this赋值给self this.timer = setTimeout(function(){ self.clearBoard(); }, 0); };
也可使用bind方法来传递this:
function testFunction () { this.clearLocalStorage(); this.timer = setTimeout(this.reset.bind(this), 0); // bind to 'this' }; function testFunction(){ this.clearBoard(); //back in the context of the right 'this'! };
在Chrome里,有几种状况会发生这个错误,其中一个就是函数的递归调用,而且不能终止。这个错误能够在Chrome开发者控制台重现。
还有,若是传给函数的值超出可接受的范围时,也会出现这个错误。不少函数只接受指定范围的数值,例如,Number.toExponential(digits)和Number.toFixed(digits)方法,只接受0到20的数值,而Number.toPrecision(digits)只接受1到21的数值。
var a = new Array(4294967295); //OK var b = new Array(-1); //range error var num = 2.555555; document.writeln(num.toExponential(4)); //OK document.writeln(num.toExponential(-2)); //range error! num = 2.9999; document.writeln(num.toFixed(2)); //OK document.writeln(num.toFixed(25)); //range error! num = 2.3456; document.writeln(num.toPrecision(1)); //OK document.writeln(num.toPrecision(22)); //range error!
来自网友的备注:
在Chrome中,若是读取未定义变量的长度属性,会报错。
若是数组未初始化,或者由于做用域的问题而没有正确地获取到,则可能会遇到此错误。让咱们用下面的例子来理解这个错误。
var testArray= ["Test"]; function testFunction(testArray) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); } } testFunction();
函数的参数名会覆盖全局的变量名。也就是说,全局的testArray被函数的参数名覆盖了,因此在函数体里访问到的是本地的testArray,但本地并无定义testArray,因此出现了这个错误。
有两种方法可用于解决这个问题:
将函数的参数移除
var testArray = ["Test"]; /* Precondition: defined testArray outside of a function */ function testFunction(/* No params */) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); } } testFunction();
把外部的变量传给函数testFunction
函数
var testArray = ["Test"]; function testFunction(testArray) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); } } testFunction(testArray);
若是对undefined变量进行赋值或读取操做,会抛出“Uncaught TypeError: cannot set property of undefined”异常。
由于test对象不存在,就会抛出“Uncaught TypeError: cannot set property of undefined”异常。
当访问一个未定义的对象或超出当前做用域的对象,就会发生这个错误。
若是在使用事件处理系统时遇到此错误,请确保使用传入的事件对象做为参数。旧浏览器(IE)提供了全局的event变量,但并非全部的浏览器都支持。像jQuery这样的库试图规范化这种行为。尽管如此,最好使用传入事件处理函数的函数。
function myFunction(event) { event = event.which || event.keyCode; if(event.keyCode===13){ alert(event.keyCode); } }
看到这里,你会发现这十大错误几乎都是null/undefined错误。若是有一个好的静态类型检查系统,好比使用TypeScript能够帮助你在编译的时候就发现问题。若是没有使用TypeScript,那么请多多使用条件语句作判断,防止这种状况出现。
在生产环境中会出现各类不可预期的错误。关键是要及时发现那些影响用户体验的错误,并使用适当的工具快速发现和解决这些问题。Fundebug提供网站bug监控,助你实时发现bug。
版权声明:
转载时请注明做者Fundebug以及本文地址:
https://blog.fundebug.com/2018/03/12/top-10-javascript-errors-from-1000-projects/