1. 优客号首页
  2. 用户投稿

来自1000多个项目的10大JavaScript错误浅析

来自1000多个项目的10大JavaScript错误浅析作为对社区开发者的回馈,我们从我们的数据库里选出了10大来自数千个项目的JavaScript错误。我们将会给出产生这些错误的根源,以及如何避免再发生这些错误。如果能够避免这些错误,就可

作为对社区开发者的回馈,我们从我们的数据库里选出了10大来自数千个项目的JavaScript错误。我们将会给出产生这些错误的根源,以及如何避免再发生这些错误。如果能够避免这些错误,就可以成为更好的开发者。

数据才是王道,我们通过收集和分析大量数据才选出了这10大JavaScript错误。我们收集每一个项目中出现的错误,并统计每一个错误发生的次数。我们根据错误代码的指纹(fingerprint)对它们进行分组,也就是说,如果第二个错误与第一个是重复的,就把它们归入同一个组。这样就可以为用户提供更好的视图,而不是像查看繁琐的日志文件那样。

我们只关注影响面最大的那些错误。为此,我们统计了错误在各个公司的项目中发生的次数,而不是错误发生的总次数,因为如果是这样的话,读者就可能看到大量与他们不相干的统计信息。

以下是排名靠前的10大JavaScript错误:

来自1000多个项目的10大JavaScript错误浅析

出于可读性方面的考虑,每个错误的描述经过精简。

1.Uncaught TypeError: Cannot read property

如果你是一名JavaScript开发者,对这个错误可能已经熟视无睹。在Chrome里读取未定义对象的属性或调用未定义对象的方法时就会发生这个错误,在Chrome开发者控制台可以很容易地重现这个错误。

来自1000多个项目的10大JavaScript错误浅析

发生这个错误的原因有很多,其中最为常见的是,在渲染UI组件时没有正确初始化状态。我们通过一个真实的例子来看看这个错误是怎么发生的。我们选择React作为示例,不过在其他框架(Angular、Vue等)中也是一样的。

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> ); }}

这里要注意两件事:

组件的状态(如this.state)在一开始就是undefined。 如果是通过异步的方式来加载数据,那么在数据加载进来之前,至少要渲染一次组件——不管是在构造器、componentWillMout()还是componentDidMout()中加载数据。Quiz在进行第一次渲染时,this.state.items是undefined,那么ItemList就会得到undefined的数据项,这样就会在控制台看到这个错误——“Uncaught TypeError:Cannot read property ‘map’ of undefined”。

要解决这个问题其实很简单,在构造器里使用适当的默认值进行初始化。

class Quiz extends Component { // 增加这个: constructor(props) { super(props); // 使用空数组给state赋值 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> ); }} 2. TypeError: ’undefined’ is not an object

在Safari里读取未定义对象的属性或调用未定义对象的方法时就会发生这个错误,在Safari开发者控制台可以很容易地重现这个错误。这个错误与发生在Chrome里的是差不多的,只是Safari为它提供了不同的错误信息。

来自1000多个项目的10大JavaScript错误浅析

3. TypeError: null is not an object

在Safari里读取空(null)对象的属性或调用空对象的方法时就会发生这个错误,在Safari开发者控制台可以很容易地重现这个错误。

来自1000多个项目的10大JavaScript错误浅析

有意思的是,在JavaScript里,null和undefined其实是不一样的,所以我们会看到两个不同的错误消息。undefined表示未赋值的变量,而null表示变量值为空。可以使用严格等于号来证明它们不是同一个东西。

来自1000多个项目的10大JavaScript错误浅析

在实际应用当中,在JavaScript里调用一个未加载的DOM元素就会出现这个错误。如果对象为空,DOM API就会返回null。

DOM元素需要在创建之后才能被访问。JavaScript代码是按照从上到下的顺序进行解析的,所以,如果在DOM元素之前有一个标签包含了JavaScript代码,浏览器在解析HTML时就会执行这些代码。在加载JavaScript之前,如果DOM元素没有被创建,就会出现这个错误。

在这个例子里,我们可以通过添加一个事件监听器来解决这个问题,在页面加载完毕时,事件监听器会通知我们。在addEventListener被触发之后,init()方法就可以大胆地访问DOM元素了。

<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> 4. (unknown): Script error

跨域的未捕捉JavaScript异常会变成Script error。例如,假设JavaScript托管在CDN上,那么未捕捉的错误(错误没有在try-catch里被捕获,一路直上到了window.onerror里)就会显示成“Script error”,而不是显示具体的错误消息。这是浏览器出于安全方面的考虑,防止跨域传递数据。

要想获得具体的错误信息,可以这样做:

1).使用Access-Control-Allow-Origin

将Access-Control-Allow-Origin设置成“*”,表示该资源可以被任何一个域访问。如果有必要,可以把“*”替换成你的域名,例如Access-Control-Allow-Origin: www.example.com。不过,如果使用了CDN,那么要支持多个域名可能就会得不偿失,因为CDN存在缓存问题。

下面是在各种环境如何设置该字段的示例:

Apache

在JavaScript文件所在的目录创建一个叫作.htaccess的文件,并加入如下内容:

Header add Access-Control-Allow-Origin “*”

Nginx

在JavaScript对应的location配置代码块中加入add_header指令:

location ~ ^/assets/ { add_header Access-Control-Allow-Origin *;}

HAProxy

在JavaScript文件对应的backend配置块中加入如下内容:

rspadd Access-Control-Allow-Origin: *

2). 在script标签里设置crossorigin=“anonymous”

在每个设置了Access-Control-Allow-Origin字段的HTML页面里,将它们的script标签的crossorigin属性设置为“anonymous”。在Firefox里,如果出现了crossorigin,但没有设置Access-Control-Allow-Origin,JavaScript脚本就不会被执行。

5. TypeError: Object doesn’t support property

在IE里读取未定义对象的属性或调用未定义对象的方法时就会发生这个错误,在IE开发者控制台可以很容易地重现这个错误。

来自1000多个项目的10大JavaScript错误浅析

这个错误与Chrome里的“TypeError: ‘undefined’ is not a function”是同一个东西。不同的浏览器为相同的错误提供的错误消息可能是不一样的。

在IE里使用JavaScript的命名空间时,就很容易碰到这个错误。发生这个错误十有八九是因为IE无法将当前命名空间里的方法绑定到this关键字上。例如,假设有个命名空间Rollbar,它有一个方法叫isAwesome()。在Rollbar命名空间中,可以直接使用this关键字来调用这个方法:

this.isAwesome();

在Chrome、Firefox和Opera中这样做都是没有问题的,但在IE中就不行。所以,最安全的做法是指定全命名空间:

Rollbar.isAwesome(); 6. TypeError: ‘undefined’ is not a function

在Chrome里调用一个未定义的函数时就会发生这个错误,可以在Chrome开发者控制台和Mozilla开发者控制台重现这个错误。

来自1000多个项目的10大JavaScript错误浅析

近年来,JavaScript的编码技术和设计模式变得日趋复杂,回调和闭包中的自引用情况越来越普遍,让人搞不清楚代码中的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()的匿名函数的上下文实际上是window,而window并不包含clearBoard()方法。

对于旧浏览器,以往的解决办法是将this赋值给某个变量,然后在闭包里使用这个变量。例如:

function testFunction () { this.clearLocalStorage(); var self = this; // 将this赋值给self 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(); // 以’this’作为上下文}; 7. Uncaught RangeError: Maximum call stack

在Chrome里,有几种情况会发生这个错误,其中一个就是无限递归调用一个函数。这个错误可以在Chrome开发者控制台重现。

来自1000多个项目的10大JavaScript错误浅析

当传给函数的值超出可接受的范围时也会出现这个错误。很多函数只接受指定范围的数值,例如,Number.toExponential(digits)和Number.toFixed(digits)只接受0到20的数值,而Number.toPrecision(digits)只接受1到21的数值。

var a = new Array(4294967295); //OKvar b = new Array(-1); //range errorvar num = 2.555555;document.writeln(num.toExponential(4)); //OKdocument.writeln(num.toExponential(-2)); //range error!num = 2.9999;document.writeln(num.toFixed(2)); //OKdocument.writeln(num.toFixed(25)); //range error!num = 2.3456;document.writeln(num.toPrecision(1)); //OKdocument.writeln(num.toPrecision(22)); //range error! 8. TypeError: Cannot read property ‘length’

在Chrome里读取undefined变量的length属性时会发生这个错误,这个错误可以在Chrome开发者控制台重现。

来自1000多个项目的10大JavaScript错误浅析

length是数组的属性,但如果数组没有初始化或者数组的变量名被另一个上下文隐藏起来的话,访问length属性就会发生这个错误。例如:

var testArray= [“Test”];function testFunction(testArray) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); }}testFunction();

函数的参数名会覆盖全局的变量名。也就是说,全局的testArray被函数的参数名覆盖了,所以在函数体里访问到的是本地的testArray,但本地并没有定义testArray,所以出现了这个错误。

有两种方法可用于解决这个问题:

1). 将函数的参数名移除(这就表示函数里要访问的变量已经在函数外面定义好了,所以函数不需要参数):

var testArray = [“Test”];/* 前提是要在函数外面定义好testArray */function testFunction(/* No params */) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); }}testFunction();

2). 在调用函数时将变量传递进去:

var testArray = [“Test”];function testFunction(testArray) { for (var i = 0; i < testArray.length; i++) { console.log(testArray[i]); }}testFunction(testArray); 9. Uncaught TypeError: Cannot set property

我们无法对undefined变量进行赋值或读取操作,否则的话会抛出“Uncaught TypeError: cannot set property of undefined”异常。

例如,在Chrome中:

来自1000多个项目的10大JavaScript错误浅析

如果test对象不存在,就会抛出“Uncaught TypeError: cannot set property of undefined”异常。

10. ReferenceError: event is not defined

在访问一个未定义的对象或超出当前作用域的对象时就会发生这个错误,这个错误可以在Chrome开发者控制台重现。

来自1000多个项目的10大JavaScript错误浅析

如果在进行事件处理时遇到这个错误,请确保事件对象被作为参数传入到函数当中。旧浏览器(IE)提供了全局的event变量,但并不是所有的浏览器都会这样。尽管jQuery尝试对这种行为进行规范化,但最好还是使用传给函数的event对象:

function myFunction(event) { event = event.which || event.keyCode; if(event.keyCode===13){ alert(event.keyCode); }}

结论

我们希望这些内容能够帮助大家在未来避免这些错误,解决大家的痛点。不过,即使有了这些最佳实践,在生产环境中仍然会出现各种不可预期的错误。关键是要及时发现那些影响用户体验的错误,并使用适当的工具快速解决这些问题。

查看英文原文: Top 10 JavaScript errors from 1000+ projects (and how to avoid them)

感谢徐川对本文的审校。

 

来自:http://www.infoq.com/cn/articles/top-10-javascript-errors

 

前端错误Uncaught TypeError: Cannot read property ‘length’ of null错误怎么处理?

苹果调整iOS16界面UI适配机型(iOS16系统或将首发登场)

通过统计数据库中的1000多个项目,我们发现在 JavaScript 中最常出现的错误有10个。下面会向大家介绍这些错误发生的原因以及如何防止。

当你读取一个属性或调用一个未定义对象的方法时,Chrome 中就会报出这样的错误。导致这个错误发生的原因有很多,常见的一种情况是在渲染 UI 组件时,不正确地初始化状态。我们来看一个真实的应用程序中发生这种情况的例子。以上代码有两个重要方面:一是组件的状态(例如 this.state),在开始生命周期之前是 undefined 状态。

二是当通过异步的方式获取数据时,无论是在构造函数中 componentWillMount 中,还是在构造函数中提取 componentDidMount,组件在数据加载之前至少会渲染一次。当检测首次渲染时,会发现 this.state.items 是未定义的。此时就会出现一个错误 -“Uncaught TypeError: Cannot read property ‘map’ of undefined” in the consol”。

解决的方法很简单:在构造函数中使用合理的默认值进行状态初始化。2. TypeError: ‘undefined’ Is Not an Object (evaluating…)这是在 Safari 中读取属性或调用未定义对象上的方法时发生的错误,这与 Chrome 的上述错误基本相同,只是 Safari 使用不同的错误消息。3. TypeError: Null Is Not an Object (evaluating…)这是在 Safari 中读取属性或调用空对象上的方法时发生的错误。

在实际情况中,导致这种错误的原因之一是:在元素加载之前,就尝试在 JavaScript 中使用 DOM 元素。这是因为 DOM API 对于空白的对象引用返回 null。任何执行和处理 DOM 元素的 JS 代码,都应该在创建 DOM 元素之后执行。

JS 代码按照 HTML 中的规定自上而下进行解释。因此,如果在 DOM 元素之前存在标签,则脚本标签内的 JS 代码就会在浏览器分析 HTML 页面时执行。如果在加载脚本之前尚未创建 DOM 元素,就会出现这样的错误。在这个例子中,我们可以通过添加一个事件侦听器来解决这个问题,事件侦听器会在页面准备就绪时通知我们。

一旦 addEventListener 被触发,该 init( ) 方法就可以使用 DOM 元素。4. (unknown): Script Error当未捕获的 JavaScript 错误违背跨边界原则时,就会发生脚本错误。例如,如果将 JavaScript 代码托管在 CDN 上,则任何未被捕获的错误(通过 window.onerror 处理程序发出的错误,而不是 try-catch 中捕获到的错误)将仅报告为“脚本错误”。这是浏览器的一种安全措施,主要用于防止跨域传递数据的情况出现。

要获取真实的错误消息,需要执行以下操作:Access-Control-Allow-Origin将 Access-Control-Allow-Origin 设置为 *, 表示可以从任何域正确访问资源。* 如有必要,也可以用自己的域名进行替换,例如:Access-Control-Allow-Origin: www.example.com。以下是在各种环境中设置的一些示例:在脚本标签上设置crossorigin =“anonymous”在你的 HTML 源代码中,为每一个脚本设置 Access-Control-Allow-Origin,在设置 SCRIPT 标签中,设置 crossorigin=”anonymous”。在将 crossorigin 属性添加到脚本标签之前,请确保正在向脚本文件发送 header。

在 Firefox 中,如果 crossorigin 属性存在但 Access-Control-Allow-Origin 标题不存在,则脚本不会执行。5. TypeError: Object Doesn’t Support Property当调用未定义的方法时,IE 中会发生这样的错误。这相当于 Chrome 中的 “undefined’ is not a function” 错误。

对于相同的逻辑错误,不同的浏览器可能会有不同的错误消息。这是在 IE 的 Web 应用程序中使用 JavaScript 命名空间出现的一个常见问题。出现这种情况的绝大部分原因是IE无法将当前名称空间内的方法绑定到this关键字。

例如,如果你有 JS Rollbar 方法的命名空间 isAwesome。通常,如果位于 Rollbar 命名空间内,则可以使用以下语法调用该 isAwesome 方法:6. TypeError: ‘undefined’ Is Not a Function当调用未定义的函数时,Chrome 中就会发生这样的错误。执行上面的代码会导致以下错误:“Uncaught TypeError: undefined is not a function。” 发生以上错误的原因是,当你调用 setTimeout( ) 时,实际上是在调用 window.setTimeout( ),传递给 setTimeout( ) 的匿名函数是在窗口对象的上下文中定义的,而该窗口对象没有 clearBoard( ) 方法。

符合旧版浏览器的解决方案是以变量的方式简单地将引用保存在 this 中,然后通过闭包继承。

javascript有哪些不合理的设计缺陷

苹果调整iOS16界面UI适配机型(iOS16系统或将首发登场)

一.为什么Javascript有设计缺陷?
这里有三个客观原因,导致Javascript的设计不够完善。

1. 设计阶段过于仓促
Javascript的设计,其实只用了十天。

另一方面,这种语言的设计初衷,是为了解决一些简单的网页互动(比如,检查”用户名”是否填写),并没有考虑复杂应用的需要。设计者做梦也想不到,Javascript将来可以写出像Gmail这种极其庞大复杂的网页。
2. 没有先例
Javascript同时结合了函数式编程和面向对象编程的特点,这很可能是历史上的第一例。而且直到今天为止,Javascript仍然是世界上唯一使用Prototype继承模型的主要语言。

这使得它没有设计先例可以参考。
3. 过早的标准化
Javascript的发展非常快,根本没有时间调整设计。
1995年5月,设计方案定稿;10月,解释器开发成功;12月,向市场推出,立刻被广泛接受,全世界的用户大量使用。

Javascript缺乏一个从小到大、慢慢积累用户的过程,而是连续的爆炸式扩散增长。大量的既成网页和业余网页设计者的参与,使得调整语言规格困难重重。
更糟的是,Javascript的规格还没来及调整,就固化了。

1996年8月,微软公司强势介入,宣布推出自己的脚本语言Jscript;11月,为了压制微软,网景公司决定申请Javascript的国际标准;1997年6月,第一个国际标准ECMA-262正式颁布。
也就是说,Javascript推出一年半之后,国际标准就问世了。设计缺陷还没有充分暴露就成了标准。

相比之下,C语言问世将近20年之后,国际标准才颁布。

二.Javascript的10个设计缺陷

1. 不适合开发大型程序
Javascript没有名称空间(namespace),很难模块化;没有如何将代码分布在多个文件的规范;允许同名函数的重复定义,后面的定义可以覆盖前面的定义,很不利于模块化加载。
2. 非常小的标准库
Javascript提供的标准函数库非常小,只能完成一些基本操作,很多功能都不具备。
3. null和undefined
null属于对象(object)的一种,意思是该对象为空;undefined则是一种数据类型,表示未定义。

typeof null; // object
typeof undefined; // undefined
两者非常容易混淆,但是含义完全不同。
var foo;
alert(foo == null); // true
alert(foo == undefined); // true
alert(foo === null); // false
alert(foo === undefined); // true
在编程实践中,null几乎没用,根本不应该设计它。
4. 全局变量难以控制
Javascript的全局变量,在所有模块中都是可见的;任何一个函数内部都可以生成全局变量,这大大加剧了程序的复杂性。
a = 1;
(function(){
b=2;
alert(a);
})(); // 1
alert(b); //2
5. 自动插入行尾分号
Javascript的所有语句,都必须以分号结尾。

但是,如果你忘记加分号,解释器并不报错,而是为你自动加上分号。有时候,这会导致一些难以发现的错误。
比如,下面这个函数根本无法达到预期的结果,返回值不是一个对象,而是undefined。
function(){
return{i=1};
}
原因是解释器自动在return语句后面加上了分号。

function(){
return;{i=1};
}
6. 加号运算符
+号作为运算符,有两个含义,可以表示数字与数字的和,也可以表示字符与字符的连接。
alert(1+10); // 11
alert(“1″+”10″); // 110
如果一个操作项是字符,另一个操作项是数字,则数字自动转化为字符。
alert(1+”10”); // 110
alert(“10″+1); // 101
这样的设计,不必要地加剧了运算的复杂性,完全可以另行设置一个字符连接的运算符。

7. NaN
NaN是一种数字,表示超出了解释器的极限。它有一些很奇怪的特性:
NaN === NaN; //false
NaN !== NaN; //true
alert( 1 + NaN ); // NaN
与其设计NaN,不如解释器直接报错,反而有利于简化程序。
8. 数组和对象的区分
由于Javascript的数组也属于对象(object),所以要区分一个对象到底是不是数组,相当麻烦。

Douglas Crockford的代码是这样的:
if ( arr &&typeof arr === ‘object’ &&typeof arr.length === ‘number’ &&!arr.propertyIsEnumerable(‘length’)){
alert(“arr is an array”);
}
9. == 和 ===
==用来判断两个值是否相等。当两个值类型不同时,会发生自动转换,得到的结果非常不符合直觉。
“” == “0” // false
0 == “” // true
0 == “0” // true
false == “false” // false
false == “0” // true
false == undefined // false
false == null // false
null == undefined // true
” \t\r\n” == 0 // true
因此,推荐任何时候都使用”===”(精确判断)比较符。
10. 基本类型的包装对象
Javascript有三种基本数据类型:字符串、数字和布尔值。

它们都有相应的建构函数,可以生成字符串对象、数字对象和布尔值对象。
new Boolean(false);
new Number(1234);
new String(“Hello World”);
与基本数据类型对应的对象类型,作用很小,造成的混淆却很大。
alert( typeof 1234); // number
alert( typeof new Number(1234)); // object
关于Javascript的更多怪异行为,请参见Javascript Garden和wtfjs.com。


三.如何看待Javascript的设计缺陷?

既然Javascript有缺陷,数量还不少,那么它是不是一种很糟糕的语言?有没有前途?
回答是Javascript并不算糟糕,相反它的编程能力很强大,前途很光明。
首先,如果遵守良好的编程规范,加上第三方函数库的帮助,Javascript的这些缺陷大部分可以回避。
其次,Javascript目前是网页编程的唯一语言,只要互联网继续发展,它就必然一起发展。目前,许多新项目大大扩展了它的用途,node.js使得Javascript可以用于后端的服务器编程,coffeeScript使你可以用python和ruby的语法,撰写Javascript。

最后,只要发布新版本的语言标准(比如 ECMAscript 5),就可以弥补这些设计缺陷。当然,标准的发布和标准的实现是两回事,上述的很多缺陷也许会一直伴随到Javascript存在的最后一天。

javascript错误怎么办

苹果调整iOS16界面UI适配机型(iOS16系统或将首发登场)

Javascript 错误处理的几种方法1.使用window.onerror指定错误处理函数。 当有错误的时候,onerror会被callback。

如: 代码如下:<html xmlns=”http://www.w3.org/1999/xhtml”> <head> <title>Test</title> <script type=”text/javascript”> window.onerror = function(message, url, line) { alert(“Error.\nMessage:”+ message +”\nUrl:” + url + “\nLine:” + line) return true; } </script> </head> <body> <script type=”text/javascript”> test(); test(); test(); test(); </script> <script type=”text/javascript”> test(); test(); test(); test(); </script> </body> </html>在上面的例子中只会有每一个block中的第一个test();产生error。触发window.onerror回调,后面的Javascript会被忽略掉。img 也支持 onerror < img src=”pic.gif” onerror = “javascript:alert(“An error occurred.”);”/>。onerror 是浏览器支持的对象。

由浏览器决定是否可以使用,不是DOM标准。 2.使用Javascript中的try catch throw处理异常。 Javascript支持了try catch throw,Javascript中定义的异常: (1)EvalError: An error occurs in the eval() function. (2)RangeError: A number value is greater then or less then the number that can be represented in Javascript(Number.MAX_VALUE and Number.MIN_VAKUE). (3)ReferenceError: An illegal reference is used. (4)SyntaxError: A syntax error occus inside of an eval() function call. All other syntax error are reorted by the browser and cannot be handled with a try…catch statement. (5)TypeError. A variables type is unexpected. 6.URIError. An error ocuurs in the encodeURI() or the decodeURI() function. 如: 代码如下:<script type=”text/javascript”> function CreateError() { throw new Error(“Created error by custom.”); } try { //throw a error from a function just want to see the call stack in firefox. CreateError(); } catch(error) { var errorMsg = (“Message: ” + error.message + “\n”); if(typeof(error.stack)!=undefined) { //FF errorMsg += (“Line Number: ” + error.lineNumber + “\n”); errorMsg += (“File Name: ” + error.fileName + “\n”); errorMsg += (“Stack Trace:\n” + error.stack + “\n”); } else { //IE errorMsg += (“Description: ” + error.description + “\n”); errorMsg += (“Number: ” + error.number + “\n”); } alert(errorMsg); } finally { //alert(“End try catch.message from finally block.”); } </script> Error.message是IE和FireFox都支持的属性。

IE支持description 和 number属性。 FF支持fileName lineNumber 和 stack 属性。 由于Javascript是弱类型的语言。

所以在catch部分只能catch一次,不能像C#这样的语言可以写多个catch,catch不同类型的exception。 但是可以用 instanceof ErrorType的方式实现类似的功能。 如: 代码如下:<script type=”text/javascript”> try { //Syntax Error //eval(“alert a”); //Custom Error throw new Error(“An error occured.”); } catch(error) { if(error instanceof SyntaxError) { alert(“Syntax Error”); } else if(error instanceof EvalError) { alert(“Eval Error”); } else if(error instanceof RangeError) { alert(“Range Error”); } else if(error instanceof ReferenceError) { alert(“Reference Error”); } else if(error instanceof TypeError) { alert(“Type Error”); } else if(error instanceof Error) { alert(“Custon Error”); } alert(error.message); } </script> 注:浏览器不会抛出Error类型的exception异常,所以如果捕获到Error类型的异常,可以确定这个异常是用户代码抛出的,不是浏览器抛出的。

如何捕获和分析 JavaScript Error

苹果调整iOS16界面UI适配机型(iOS16系统或将首发登场)

前端工程师都知道 JavaScript 有基本的异常处理能力。我们可以 throw new Error(),浏览器也会在我们调用 API 出错时抛出异常。

反正只要 JavaScript 出错后刷新不复现,那用户就可以通过刷新解决问题,浏览器不会崩溃,当没有发生过好了。这种假设在 Single Page App 流行之前还是成立的。现在的 Single Page App 运行一段时间后状态复杂无比,用户可能进行了若干输入操作才来到这里的,说刷新就刷新啊?之前的操作岂不要完全重做?所以我们还是有必要捕获和分析这些异常信息的,然后我们就可以修改代码避免影响用户体验。捕获异常的方式我们自己写的 throw new Error() 想要捕获当然可以捕获,因为我们很清楚 throw 写在哪里了。

但是调用浏览器 API 时发生的异常就不一定那么容易捕获了,有些 API 在标准里就写着会抛出异常,有些 API 只有个别浏览器因为实现差异或者有缺陷而抛出异常。对于前者我们还能通过 try-catch 捕获,对于后者我们必须监听全局的异常然后捕获。try-catch如果有些浏览器 API 是已知会抛出异常的,那我们就需要把调用放到 try-catch 里面,避免因为出错而导致整个程序进入非法状态。

例如说 window.localStorage 就是这样的一个 API,在写入数据超过容量限制后就会抛出异常,在 Safari 的隐私浏览模式下也会如此。try { localStorage.setItem(‘date’, Date.now());} catch (error) { reportError(error);}另一个常见的 try-catch 适用场景是回调。因为回调函数的代码是我们不可控的,代码质量如何,会不会调用其它会抛出异常的 API,我们一概不知道。

为了不要因为回调出错而导致调用回调后的其它代码无法执行,所以把调用回到放到 try-catch 里面是必须的。listeners.forEach(function(listener) { try { listener(); } catch (error) { reportError(error); }});window.onerror对于 try-catch 覆盖不到的地方,如果出现异常就只能通过 window.onerror 来捕获了。window.onerror = function(errorMessage, scriptURI, lineNumber) { reportError({ message: errorMessage, script: scriptURI, line: lineNumber });}注意不要耍小聪明使用 window.addEventListener 或 window.attachEvent 的形式去监听 window.onerror。

很多浏览器只实现了 window.onerror,或者是只有 window.onerror 的实现是标准的。考虑到标准草案定义的也是 window.onerror,我们使用 window.onerror 就好了。属性丢失假设我们有一个 reportError 函数用来收集捕获到的异常,然后批量发送到服务器端存储以便查询分析,那么我们会想要收集哪些信息呢?比较有用的信息包括:错误类型(name)、错误消息(message)、脚本文件地址(script)、行号(line)、列号(column)、堆栈跟踪(stack)。如果一个异常是通过 try-catch 捕获到的,这些信息都在 Error 对象上(主流浏览器都支持),所以 reportError 也能收集到这些信息。

但如果是通过 window.onerror 捕获到的,我们都知道这个事件函数只有 3 个参数,所以这 3 个参数以外的信息就丢失了。序列化消息如果 Error 对象是我们自己创建的话,那么 error.message 就是由我们控制的。基本上我们把什么放进 error.message 里面,window.onerror 的第一个参数(message)就会是什么。(浏览器其实会略作修改,例如加上 ‘Uncaught Error: ‘ 前缀。

)因此我们可以把我们关注的属性序列化(例如 JSON.Stringify)后存放到 error.message 里面,然后在 window.onerror 读取出来反序列化就可以了。当然,这仅限于我们自己创建的 Error 对象。第五个参数浏览器厂商也知道大家在使用 window.onerror 时受到的限制,所以开始往 window.onerror 上面添加新的参数。考虑到只有行号没有列号好像不是很对称的样子,IE 首先把列号加上了,放在第四个参数。

然而大家更关心的是能否拿到完整的堆栈,于是 Firefox 说不如把堆栈放在第五个参数吧。但 Chrome 说那还不如把整个 Error 对象放在第五个参数,大家想读取什么属性都可以了,包括自定义属性。结果由于 Chrome 动作比较快,在 Chrome 30 实现了新的 window.onerror 签名,导致标准草案也就跟着这样写了。

window.onerror = function( errorMessage, scriptURI, lineNumber, columnNumber, error) { if (error) { reportError(error); } else { reportError({ message: errorMessage, script: scriptURI, line: lineNumber, column: columnNumber }); }}属性正规化我们之前讨论到的 Error 对象属性,其名称都是基于 Chrome 命名方式的,然而不同浏览器对 Error 对象属性的命名方式各不相同,例如脚本文件地址在 Chrome 叫做 script 但在 Firefox 叫做 filename。因此,我们还需要一个专门的函数来对Error 对象进行正规化处理,也就是把不同的属性名称都映射到统一的属性名称上。具体做法可以参考这篇文章。

尽管浏览器实现会更新,但人手维护一份这样的映射表并不会太难。类似的是堆栈跟踪(stack)的格式。这个属性以纯文本的形式保存一份异常在发生时的堆栈信息,由于各个浏览器使用的文本格式不一样,所以也需要人手维护一份正则表达,用于从纯文本中提取每一帧的函数名(identifier)、文件(script)、行号(line)和列号(column)。安全限制如果你也遇到过消息为 ‘Script error.’ 的错误,你会明白我在说什么的,这其实是浏览器针对不同源(origin)脚本文件的限制。

这个安全限制的理由是这样的:假设一家网银在用户登录后返回的 HTML 跟匿名用户看到的 HTML 不一样,一个第三方网站就能把这家网银的 URI 放到 script.src 属性里面。HTML 当然不可能被当做 JS 解析啦,所以浏览器会抛出异常,而这个第三方网站就能通过解析异常的位置来判断用户是否有登录。为此浏览器对于不同源脚本文件抛出的异常一律进行过滤,过滤得只剩下 ‘Script error.’ 这样一条不变的消息,其它属性统统消失。

对于有一定规模的网站来说,脚本文件放在 CDN 上,不同源是很正常的。现在就算是自己做个小网站,常见框架如 jQuery 和 Backbone 都能直接引用公共 CDN 上的版本,加速用户下载。所以这个安全限制确实造成了一些麻烦,导致我们从 Chrome 和 Firefox 收集到的异常信息都是无用的 ‘Script error.’。CORS想要绕过这个限制,只要保证脚本文件和页面本身同源即可。

但把脚本文件放在不经 CDN 加速的服务器上,岂不降低用户下载速度?一个解决方案是,脚本文件继续放在 CDN 上,利用 XMLHttpRequest 通过 CORS 把内容下载回来,再创建 <script> 标签注入到页面当中。在页面当中内嵌的代码当然是同源的啦。这说起来很简单,但实现起来却有很多细节问题。

用一个简单的例子来说:<script src=”http://cdn.com/step1.js”></script><script> (function step2() {})();</script><script src=”http://cdn.com/step3.js”></script>我们都知道这个 step
1.step
2.step3 如果存在依赖关系的话,则必须严格按照这个顺序执行,否则就可能出错。浏览器可以并行请求 step1 和 step3 的文件,但在执行时顺序是保证的。如果我们自己通过 XMLHttpRequest 获取 step1 和 step3 的文件内容,我们就需要自行保证其顺序正确性。此外不要忘记了 step2,在 step1 以非阻塞形式下载的时候 step2 就可以被执行了,所以我们还必须。

javascript错误

苹果调整iOS16界面UI适配机型(iOS16系统或将首发登场)

var

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表优客号立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:https://www.youkehao.org.cn/article/55150.html

如若内容造成侵权/违法违规/事实不符,请联系优客号进行投诉反馈,一经查实,立即删除!

发表评论

登录后才能评论