您的位置:首页 » 分类: JavaScript » 文章: 10 个最常见的 JavaScript 错误(以及如何避免它们)- 来自 Rollbar 1000 多个项目的统计

10 个最常见的 JavaScript 错误(以及如何避免它们)- 来自 Rollbar 1000 多个项目的统计

小编推荐:掘金是一个面向程序员的高质量技术社区,从 一线大厂经验分享到前端开发最佳实践,无论是入门还是进阶,来掘金你不会错过前端开发的任何一个技术干货。

为了回馈我们的开发者社区,我们查看了数千个项目的数据库,发现了 JavaScript 的 10 大错误。我们将向你展示这些错误的原因,以及如何防止这些错误发生。如果你避免了这些 “陷阱” ,这将使你成为一个更好的开发人员。

由于数据是国王,我们收集,分析并排名前十的 JavaScript 错误。 Rollbar 会收集每个项目的所有错误,并总结每个项目发生的次数。 我们根据 指纹 对错误进行分组,来做到这一点。基本上,如果第二个错误只是第一个错误的重复,我们会把两个错误分到同一组。 这给用户一个很好的概括,而不是像在日志文件中看到的那些压迫性的一大堆垃圾描述。

我们专注于最有可能影响你和你的用户的错误。 为此,我们通过横跨不同公司的项目数来排列错误。 如果我们只查看每个错误发生的总次数,那么大流量的项目可能会淹没与大多数读者无关的错误的数据集。

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

排名前10 的 JavaScript 错误

为了便于阅读,没有花大段的文字来描述每个错误。让我们深入到每一个错误,来确定什么可以导致它,以及如何避免它发生。

1.Uncaught TypeError: Cannot read property

如果你是一个 JavaScript 开发人员,你可能已经看到这个错误的次数比你敢承认的要多。当你读取一个属性或调用一个未定义的对象的方法时,这个错误会在 Chrome 中发生。你可以在 Chrome 开发者工具的控制台中轻松测试。

JavaScript 错误 - Uncaught TypeError: Cannot read property

发生这种情况的原因很多,但常见的一种情况是在渲染UI组件时不恰当地初始化了 state(状态)。 我们来看一个在真实应用程序中如何发生的例子。 我们将选择 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>
);
}
}

这里有两件重要的事情要实现:

  1. 组件的状态(例如 this.state)从 undefined 开始。
  2. 当您异步获取数据时,组件在数据加载之前至少会渲染一次,而不管它是在构造函数 componentWillMount 还是 componentDidMount 中获取的。 当 Quiz 第一次渲染时,this.state.itemsundefined 。 这又意味着 ItemList 将 items 定义为 undefined ,并且在控制台中出现错误 – “Uncaught TypeError: Cannot read property ‘map’ of undefined”。

这个问题很容易解决。最简单的方法:在构造函数中用合理的默认值来初始化 state。

class Quiz extends Component {
// 添加这些代码:
constructor(props) {
super(props);
// 分配 state(状态) 本身,以及 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>
);
}
}

你的应用中的确切代码可能会有所不同,但是我们希望我们已经给了你足够的线索,来解决或避免在你的应用程序中出现这个问题。如果你还没有碰到,请继续阅读,因为我们将在下面覆盖更多相关错误的示例。

2.TypeError: ‘undefined’ is not an object (evaluating

这是在 Safari 中读取属性或调用未定义对象上的方法时发生的错误。你可以在 Safari Developer Console 中轻松测试。这与 Chrome 的上述错误基本相同,但 Safari 使用不同的错误消息。

JavaScript 错误 - TypeError: ‘undefined’ is not an object (evaluating

3.TypeError: null is not an object (evaluating

这是在Safari中读取属性或调用 空对象(null) 上的方法时发生的错误。您可以在 Safari Developer Console中轻松测试。

JavaScript 错误 - TypeError: null is not an object (evaluating

有趣的是,在 JavaScript 中,null 和 undefined 是不一样的,这就是为什么我们看到两个不同的错误信息。 undefined 通常是一个尚未分配的变量,而 null 表示该值为空。 要验证它们不相等,请尝试使用严格相等运算符 ===

JavaScript 错误 - null == undefined 和 null === undefined

在现实的例子中,这种错误可能发生的一种场景是:如果在加载元素之前尝试在 JavaScript 中使用 DOM 元素。这是因为 DOM API 对于空白的对象引用返回 null 。

任何执行和处理 DOM 元素的 JS 代码都应在 DOM 元素创建后执行。JS 代码按照 HTML 中的规定从上到下进行解析。所以,如果 DOM 元素之前有一个 script 标签, script 标签内的JS代码将在浏览器解析 HTML 页面时执行。如果在加载脚本之前尚未创建 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 错误违反了跨域策略时,就会出现这类脚本错误。例如,如果你将 JavaScript 代码托管在 CDN 上,任何未被捕获的错误(这个会冒泡到 window.onerror 处理程序,而不是在 try-catch 捕获)将被报告为简单的 “脚本错误” ,而不会包含有用的信息。这是一种浏览器安全措施,旨在防止跨域传递数据,否则将不允许进行通信。

如果你要获取到真实的错误消息,请执行以下操作:

  1. 发送 Access-Control-Allow-Origin 头信息

Access-Control-Allow-Origin 头信息设置为 * ,表示可以从任何域正确访问资源。如有必要,您可以用你的域名替换 *,例如 Access-Control-Allow-Origin: www.example.com 。但是,处理多个域名会有些棘手,如果你使用 CDN ,由此出现的缓存问题可能会让你感觉不值得付出努力。 点击这里 看到更多。

下面是一些如何在不同环境中设置 Access-Control-Allow-Origin 头信息的例子。

Apache

在 JavaScript 文件所在的文件夹中,使用以下内容创建一个 .htaccess 文件:

Header add Access-Control-Allow-Origin "*"

Nginx

将 add_header 指令添加到提供 JavaScript 文件的位置块中:

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

HAProxy

将以下内容添加到提供资源服务的后端,并提供 JavaScript 文件:

rspadd Access-Control-Allow-Origin:\ *
  1. 在 script 标签上设置 crossorigin="anonymous" 属性

在你的 HTML 源代码中,对于你设置的 Access-Control-Allow-Origin 头信息的每个脚本,在 script 标签上设置 crossorigin="anonymous" 。在添加脚本标记上的 crossorigin 属性之前,请确保验证上述头信息是否正确发送。在 Firefox 中,如果存在 crossorigin 属性,但 Access-Control-Allow-Origin 头信息不存在,则脚本将不会执行。

5.TypeError: Object doesn’t support property

这是你在调用未定义方法时发生在IE中的错误。你可以在IE开发者工具的控制台进行测试。

JavaScript 错误 - TypeError: Object doesn’t support property

这相当于 Chrome 中的错误:”TypeError: ‘undefined’ is not a function” 。是的,对于相同的逻辑错误,不同的浏览器可能会有不同的错误消息。

在使用 JavaScript 命名空间的Web应用程序中,这中错误对于 IE 来说是一个常见问题。在这种情况下,这种问题 99.9% 是 IE 无法将当前名称空间内的方法绑定到 this 关键字。例如,如果你的 JS 命名空间 Rollbar 中有 isAwesome 方法。通常,如果你在 Rollbar 命名空间内,则可以使用以下语法调用 isAwesome 方法:

this.isAwesome();

Chrome,Firefox 和 Opera 会欣然地接受这个语法。 IE 则不会。 因此,使用 JS 命名空间时最安全的选择是始终以实际命名空间作为前缀。

Rollbar.isAwesome();

6.TypeError: ‘undefined’ is not a function

当你调用未定义的函数时,在 Chrome 中会发生这种错误。 你可以在 Chrome 开发者工具的控制台和 Mozilla Firefox 开发者工具的控制台中对此进行测试。

JavaScript 错误 - TypeError: ‘undefined’ is not a function

随着 JavaScript 编码技术和设计模式在这些年来越来越复杂,回调和闭包内的自引用作用域也相应增加,这是使用 this/that 混乱的一个相当常见的原因。

考虑这个示例代码片段:

function clearBoard(){
alert("Cleared");
}
document.addEventListener("click", function(){
this.clearBoard(); // what is “this” ?
});

如果你执行上面的代码然后点击页面,会导致以下错误: “Uncaught TypeError: this.clearBoard is not a function”。原因是正在执行的匿名函数在 document 上下文中, 而 clearBoard 定义在 window 中。

一个传统的,旧浏览器兼容的解决方案是简单地将你的 this 保存在一个变量,然后该变量可以被闭包继承。 例如:

var self=this;  // save reference to 'this', while it's still this!
document.addEventListener("click", function(){
self.clearBoard();
});

或者,在较新的浏览器中,可以使用 bind() 方法传递适当的引用:

document.addEventListener("click",this.clearBoard.bind(this));

7.Uncaught RangeError: Maximum call stack

这是 Chrome 在一些情况下会发生的错误。一个情况是当你调用一个不终止的递归函数时。你可以在Chrome开发者工具的控制台中进行测试。

JavaScript 错误 - Uncaught RangeError: Maximum call stack

如果你一个将值传递给超出范围的函数,也可能会发生这种情况。许多函数只接受其输入值的特定范围的数字。
例如,Number.toExponential(digits)Number.toFixed(digits) 接受0到20之间的数字,
Number.toPrecision(digits) 接受从1到21的数字。

此外,如果您将值传递给超出范围的函数,也可能会发生这种情况。 许多函数只接受其输入值的特定范围的数字。 例如: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!

8.TypeError: Cannot read property ‘length’

这是 Chrome 中发生的错误,因为读取未定义变量的长度属性。你可以在Chrome开发者工具的控制台中进行测试。

JavaScript 错误 - TypeError: Cannot read property ‘length’

你通常会在数组中找到定义的长度,但是如果数组未初始化或变量名隐藏在另一个上下文中,则可能会遇到此错误。让我们用下面的例子来理解这个错误。

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

当你用参数声明一个函数时,这些参数变成了本地参数。这意味着即使你有名称为 testArray 的变量,函数中具有相同名称的参数仍将被视为 本地参数

你有两种方法可以解决这个问题:

  1. 删除函数声明语句中的参数(事实证明如果你想访问那些在函数之外声明的变量,你不需要将其作为你函数的参数传入):
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();
  1. 调用函数时,将我们声明的数组传递给它:
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 ,我们不能获取或设置任何 undefined 的属性。在这种情况下,应用程序将抛出 “Uncaught TypeError cannot set property of undefined.” 错误。

例如,在Chrome浏览器中:

JavaScript 错误 - Uncaught TypeError: Cannot set property

如果 test 对象不存在,错误将会抛出 “Uncaught TypeError cannot set property of undefined.” 。

10.ReferenceError: event is not defined

当你尝试访问未定义的变量或超出当前作用域的变量时,会引发此错误。你可以在Chrome浏览器中轻松测试。

JavaScript 错误 - ReferenceError: event is not defined

如果你在使用事件处理时遇到这种错误,请确保你使用传入的事件对象作为参数。像IE这样的老浏览器提供了一个全局变量事件, Chrome 会自动将事件变量附加到处理程序。Firefox 不会自动添加它。像jQuery这样的库试图规范化这种行为。不过,最佳实践是使用传递到事件处理程序函数的方法。

document.addEventListener("mousemove", function (event) {
console.log(event);
})

总结

事实证明很多都是一些 null 或 undefined 错误。如果您使用严格的编译器选项,比如 Typescript 这样的好的静态类型检查系统可以帮助您避免它们。它可以警告你,如果一个类型是预期的,但尚未定义。

我们希望你学到了一些新的东西,并且可以避免将来出现这些错误,或者本指南帮助你解决了头痛的问题。尽管如此,即使有最佳做法,生产环境中还是会出现意想不到的错误。了解影响用户的错误非常重要,并有很好的工具来快速解决它们。

Rollbar 为你提供生产环境 JavaScript 错误的可视性,并为您提供更多上下文来快速解决它们。例如,它提供了额外的调试功能,例如 遥测功能,可以告诉你用户的浏览器发生了什么导致错误。这可以使在本地开发者工具的控制台之外发现问题。你可以在 Rollbar 的 JavaScript 应用程序的完整功能列表 中了解更多信息。

原文链接:https://rollbar.com/blog/top-10-javascript-errors/

关注WEB前端开发官方公众号

关注国内外最新最好的前端开发技术干货,获取最新前端开发资讯,致力于打造高质量的前端技术分享公众号

2 条评论 - 关于 “10 个最常见的 JavaScript 错误(以及如何避免它们)- 来自 Rollbar 1000 多个项目的统计

发表评论

电子邮件地址不会被公开。 必填项已用*标注