从1000多个项目中得到10个JavaScript错误以及如何避开它们

从1000多个项目中得到10个JavaScript错误以及如何避开它们
2018年02月04日 08:00 帅的相对论

我们将查看浏览器在JavaScript开发过程中所犯的最常见的错误,以及web开发人员可以如何避免这些错误。

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

因为数据是王,我们收集、分析和排名前10的JavaScript错误。Rollbar收集每个项目的所有错误,并总结每一个项目发生的次数。我们这样做是根据他们的指纹分类错误。基本上,如果第二个错误只是重复的第一个错误,我们将会出现两个错误。这给用户提供了一个很好的概览,而不是像您在日志文件中看到的那样巨大的转储文件。

我们关注的是最有可能影响您和您的用户的错误。为了做到这一点,我们对不同公司的项目数量进行了排名。如果我们只看每个错误发生的总次数,那么大容量的客户可能会淹没与大多数读者无关的错误的数据集。

以下是十大JavaScript错误:

为了便于阅读,每个错误都被缩短了。让我们深入研究每个问题,确定什么可以导致它,以及如何避免创建它。

1、未捕获的类型错误:不能读取属性

如果您是一个JavaScript开发人员,您可能已经看到了这个错误,而不是您愿意承认的。当您读取一个属性或在未定义的对象上调用方法时,就会出现这种情况。您可以很容易地在Chrome开发人员控制台进行测试。

这种情况可能有很多原因,但常见的原因是在呈现UI组件时不恰当地初始化状态。让我们来看看在现实世界的应用程序中如何发生这种情况的例子。我们会选择React,但是同样的不恰当初始化原则也适用于角度、Vue或其他框架。

class Quiz extends Component {

componentWillMount() {

axios.get('/thedata').then(res => {

this.setState({items: res.data});

});

}

render() {

return (

    {this.state.items.map(item =>

  • {item.name}
  • )}

);

}

}

这里有两件重要的事情:

  1. 组件的状态(例如:this.state)开始的生命是没有定义的。

  2. 当您异步地获取数据时,组件将至少在加载数据之前呈现一次——不管它是在构造函数中获取的,还是在componentWillMount或componentDidMount中获取的。当测试首次呈现时,this.state。项目是未定义的。反过来,这意味着ItemList获取未定义的项,并且您会得到一个错误——“未捕获的类型错误:无法在控制台中读取未定义的属性”映射。

这很容易解决。最简单的方法:在构造函数中使用合理的默认值初始化状态。

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 (

    {this.state.items.map(item =>

  • {item.name}
  • )}

);

}

}

你的应用程序中的具体代码可能会有所不同,但我们希望我们已经给了你足够的线索来修复或避免应用程序中的这个问题。如果不是,继续阅读,因为我们将会在下面介绍更多相关错误的例子。

2、TypeError:“未定义”不是一个对象(评估…)

这是在Safari中读取属性或调用未定义对象的方法时发生的错误。您可以在Safari开发人员控制台轻松测试这一功能。这与Chrome的上述错误本质上是相同的,但是Safari使用了不同的错误消息。

3、类型错误:Null不是一个对象(评估…)

当您读取一个属性或在空对象上调用方法时,这是在Safari中发生的错误。您可以在Safari开发人员控制台轻松测试这一功能。

有趣的是,在JavaScript中,null和未定义是不一样的,这就是为什么我们看到两个不同的错误消息。未定义的通常是一个未赋值的变量,而null则表示值为空。要验证它们不相等,请尝试使用严格的等式运算符:

在实际的示例中,可能会出现这样的错误:如果您尝试在加载元素之前使用JavaScript中的DOM元素。这是因为DOM API为空的对象引用返回null。

在创建DOM元素之后,执行和处理DOM元素的任何JS代码都应该执行。JS代码从上到下被解释为HTML中的布局。因此,如果在DOM元素之前有一个标记,那么脚本标记中的JS代码将在浏览器解析HTML页面时执行。如果在加载脚本之前没有创建DOM元素,就会得到这个错误。

在这个例子中,我们可以通过添加一个事件监听器来解决这个问题,这个事件监听器将在页面准备好时通知我们。一旦addEventListener被触发,init()方法就可以使用DOM元素。

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

}

});

4、(未知):脚本错误

当一个未捕获的JavaScript错误跨域边界违反了跨源策略时,就会出现脚本错误。例如,如果您在CDN上托管JavaScript代码,任何未捕获的错误(出现在窗口的错误)。onerror处理程序,而不是捕获在try-catch中,将被报告为简单的“脚本错误”,而不是包含有用的信息。这是一种浏览器安全措施,旨在防止跨域传递数据,否则不允许进行通信。

要获取真正的错误消息,请执行以下操作:

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

设置访问控制允许源头到*表示资源可以从任何域正确地访问。如果需要,您可以用您的域替换*,例如,访问控制允许来源:www.example.com。但是,处理多个域是很棘手的,如果您使用的是CDN,由于缓存问题可能会出现,所以可能不值得这么做。看到更多。

下面是一些如何在不同环境中设置这个头的例子:

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:\ *

2.在脚本标记上设置crossorigin="匿名"。

在您的HTML源代码中,为您设置的每个脚本都设置了访问控制允许源的标题,在脚本标记上设置crossorigin="匿名"。确保在将crossorigin属性添加到脚本标记之前,验证消息头是否被发送给脚本文件。在Firefox中,如果出现了crossorigin属性,但是访问控制允许源的头不是,脚本将不会被执行。

5、类型错误:对象不支持属性

这是在调用未定义方法时发生的错误。您可以在IE开发人员控制台进行测试。

这相当于错误的“TypeError:‘未定义’在Chrome中不是一个函数”。是的,不同的浏览器对于相同的逻辑错误可以有不同的错误消息。

对于使用JavaScript命名空间的web应用程序来说,这是一个常见的问题。当出现这种情况时,99.9%的问题是IE不能将方法绑定到当前的名称空间到这个关键字。例如,如果您使用的方法是JS名称空间滚动条,那就太棒了。通常,如果您在Rollbar名称空间内,您可以使用以下语法调用isAwesome方法:

this.isAwesome();

Chrome、Firefox和Opera都乐于接受这种语法。另一方面,也不会。因此,使用JS命名空间时最安全的方法是始终使用实际的名称空间前缀。

Rollbar.isAwesome();

6、TypeError:“未定义”不是一个函数

当你调用一个未定义的函数时,这是一个在Chrome中出现的错误。您可以在Chrome开发人员控制台和Mozilla Firefox开发人员控制台进行测试。

随着JavaScript编码技术和设计模式多年来变得越来越复杂,在回调和闭包中自引用范围的激增也相应增加,这是这种/这种混乱的一个相当常见的来源。

考虑这个示例代码片段:

function testFunction() {

this.clearLocalStorage();

this.timer = setTimeout(function() {

this.clearBoard();    // what is "this"?

}, 0);

};

执行上述代码会导致以下错误:“未捕获的TypeError:未定义的不是一个函数。“您得到上述错误的原因是,当您调用setTimeout()时,实际上是在调用window.setTimeout()。因此,将一个匿名函数传递给setTimeout()是在window对象的上下文中定义的,该对象没有clearBoard()方法。

传统的旧式浏览器兼容的解决方案是将您的引用保存在一个变量中,这个变量可以由闭包继承。例如:

function testFunction () {

this.clearLocalStorage();

var self = this;   // save reference to 'this', while it's still this!

this.timer = setTimeout(function(){

self.clearBoard();

}, 0);

};

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

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'!

};

7、未捕获的RangeError:最大调用堆栈

这是在两种情况下在Chrome中发生的错误。一个是当你调用一个没有终止的递归函数时。您可以在Chrome开发人员控制台进行测试。

如果您将一个值传递给超出范围的函数,也可能发生这种情况。许多函数只接受输入值的特定范围的数字。例如,数字,指数(数字)和数字,固定(数字)接受0到20的数字,数字,toprecision(数字)接受从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、类型错误:不能读取属性的长度

这是在Chrome中出现的一个错误,因为读取长度属性为未定义变量。您可以在Chrome开发人员控制台进行测试。

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

var testArray= ["Test"];

function testFunction(testArray) {

for (var i = 0; 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

    console.log(testArray[i]);

    }

    }

    testFunction();

  2. 调用传递函数的函数,我们声明:

    var testArray = ["Test"];

    function testFunction(testArray) {

    for (var i = 0; i

    console.log(testArray[i]);

    }

    }

    testFunction(testArray);

9、未捕获的类型错误:无法设置属性

当我们试图访问一个未定义的变量时,它总是返回未定义的,我们无法获取或设置未定义的属性。在这种情况下,应用程序将抛出“未捕获的TypeError不能设置未定义的属性”。

例如,在Chrome浏览器中:

如果测试对象不存在,则错误将抛出“未捕获的TypeError不能设置未定义的属性”。

10、ReferenceError:事件没有定义

当您尝试访问未定义的变量或超出当前范围时,将抛出此错误。你可以在Chrome浏览器中很容易地测试它。

如果在使用事件处理系统时遇到此错误,请确保使用传入的事件对象作为参数。像IE这样的老浏览器提供了一个全局变量事件,但是它不支持所有的浏览器。像jQuery这样的库试图规范这种行为。不过,最好使用传递到事件处理程序函数的方法。

function myFunction(event) {

event = event.which || event.keyCode;

if(event.keyCode===13){

alert(event.keyCode);

}

}

结论

我们希望你学到了一些新的东西,可以避免将来出现错误,或者这个指南帮助你解决了一个难题。然而,即使有最佳实践,在生产中也会出现意想不到的错误。对影响用户的错误进行可见性是很重要的,并且有好的工具可以快速地解决它们。

财经自媒体联盟更多自媒体作者

新浪首页 语音播报 相关新闻 返回顶部