对于JavaScript的错误今天进行了一个汇总,让我们更深入地研究每一个问题,以确定是什么导致了这些问题,以及如何避免产生这些问题。
1、Error实例对象
JavaScript解析或运行时,一旦发生错误,引擎就会抛出一个错误对象。JavaScript原生提供Error构造函数,所有抛出的错误都是这个构造函数的实例。
var err = new Error('出错了');
err.message // "出错了"12复制代码类型:[javascript]
上面代码中,我们调用Error构造函数,生成一个实例对象err。Error构造函数接受一个参数,表示错误提示,可以从实例的message属性读到这个参数。抛出Error实例对象以后,整个程序就中断在发生错误的地方,不再往下执行。
JavaScript语言标准只提到,Error实例对象必须有message属性,表示出错时的提示信息,没有提到其他属性。大多数JavaScript引擎,对Error实例还提供name和stack属性,分别表示错误的名称和错误的堆栈,但它们是非标准的,不是每种实现都有。
message:错误提示信息
name:错误名称(非标准属性)
stack:错误的堆栈(非标准属性)
使用name和message这两个属性,可以对发生什么错误有一个大概的了解。
if (error.name) { console.log(error.name + ': ' + error.message);
}12复制代码类型:[javascript]
stack属性用来查看错误发生时的堆栈。
function throwit() { throw new Error('');
}function catchit() { try {
throwit();
} catch(e) { console.log(e.stack); // print stack trace
}
}
catchit()// Error
// at throwit (~/examples/throwcatch.js:9:11)
// at catchit (~/examples/throwcatch.js:3:9)
// at repl:1:512345678910复制代码类型:[javascript]
上面代码中,错误堆栈的最内层是throwit函数,然后是catchit函数,最后是函数的运行环境。
2、原生错误类型
Error实例对象是最一般的错误类型,在它的基础上,JavaScript还定义了其他6种错误对象。也就是说,存在Error的6个派生对象。
3、SyntaxError对象
SyntaxError对象是解析代码时发生的语法错误。
// 变量名错误
var 1a;
// Uncaught SyntaxError: Invalid or unexpected token
// 缺少括号
console.log 'hello');
// Uncaught SyntaxError: Unexpected string123456复制代码类型:[javascript]
上面代码的错误,都是在语法解析阶段就可以发现,所以会抛出SyntaxError。第一个错误提示是“token非法”,第二个错误提示是“字符串不符合要求”。
4、ReferenceError对象
ReferenceError对象是引用一个不存在的变量时发生的错误。
// 使用一个不存在的变量
unknownVariable
// Uncaught ReferenceError: unknownVariable is not defined123复制代码类型:[javascript]
另一种触发场景是,将一个值分配给无法分配的对象,比如对函数的运行结果或者this赋值。
// 等号左侧不是变量
console.log() = 1
// Uncaught ReferenceError: Invalid left-hand side in assignment
// this 对象不能手动赋值
this = 1
// ReferenceError: Invalid left-hand side in assignment123456复制代码类型:[javascript]
上面代码对函数console.log的运行结果和this赋值,结果都引发了ReferenceError错误。
5、RangeError对象
RangeError对象是一个值超出有效范围时发生的错误。主要有几种情况,一是数组长度为负数,二是Number对象的方法参数超出范围,以及函数堆栈超过最大值。
// 数组长度不得为负数
new Array(-1)
// Uncaught RangeError: Invalid array length123复制代码类型:[java]
6、TypeError对象
TypeError对象是变量或参数不是预期类型时发生的错误。比如,对字符串、布尔值、数值等原始类型的值使用new命令,就会抛出这种错误,因为new命令的参数应该是一个构造函数。
new 123
// Uncaught TypeError: number is not a func
var obj = {};
obj.unknownMethod()
// Uncaught TypeError: obj.unknownMethod is not a function12345复制代码类型:[java]
上面代码的第二种情况,调用对象不存在的方法,也会抛出TypeError错误,因为obj.unknownMethod的值是undefined,而不是一个函数。
7、URIError对象
URIError对象是URI相关函数的参数不正确时抛出的错误,主要涉及encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()这六个函数。
decodeURI('%2')
// URIError: URI malformed12复制代码类型:[javascript]
8、EvalError对象
eval函数没有被正确执行时,会抛出EvalError错误。该错误类型已经不再使用了,只是为了保证与以前代码兼容,才继续保留。
9.UncaughtTypeError:Cannotreadproperty
如果你是一个JavaScript开发人员,你可能已经看到过这个错误。当你读取属性或在未定义对象上调用方法时,Chrome中就会发生这种情况。你可以在Chrome开发者控制台中轻松进行测试。
发生这种情况的原因有很多,但常见的原因是渲染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>
);
}
}12345678910111213141516复制代码类型:[javascript]
这里有两件重要的事情要意识到:
组件的状态(例如this.state)以undefined状态开始使用。
当你异步获取数据时,无论数据是在构造函数componentWillMount还是componentDidMount中获取,组件都将在数据加载之前至少渲染一次。当Quiz第一次渲染时,this.state.items是undefined。这反过来又意味着ItemList会得到未定义的items,你会在控制台中得到一个错误——"UncaughtTypeError:Cannotreadproperty'map'ofundefined"的错误。
这很容易解决,最简单的方法:在构造函数中使用合理的默认值初始化状态。
class Quiz extends Component {
// 添加了这个:
constructor(props) {
super(props);
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>
);
}
}1234567891011121314151617181920212223复制代码类型:[javascript]
你的应用程序中的实际代码可能会有不同,但我希望已经给了你足够的线索,让你在你的应用程序中修复或避免这个问题。如果没有,请继续阅读,因为我将在下面介绍有关相关错误的更多示例。
10.TypeError:‘undefined’isnotanobject(evaluating
这是在Safari中读取属性或调用undefined对象上的方法时发生的错误,你可以在Safari开发者控制台中非常轻松地进行测试。这基本上与上述针对Chrome的错误相同,但Safari使用了不同的错误消息。
11.TypeError:nullisnotanobject(evaluating
这是在Safari中读取属性或调用null对象上的方法时发生的错误,你可以在Safari开发者控制台中非常轻松地进行测试。
有趣的是,在JavaScript中,null和undefined不相同,这就是为什么我们看到两个不同的错误消息的原因。undefined通常是尚未分配的变量,而null表示该值为空白。要验证它们是否相等,请尝试使用严格相等运算符。
在实际示例中可能发生这种错误的一种方式是,在加载元素之前尝试在JavaScript中使用DOM元素,这是因为DOMAPI对于空白的对象引用返回null。
任何执行和处理DOM元素的JS代码都应在创建DOM元素后执行。JS代码按照HTML格式从上到下进行解释,所以,如果在DOM元素之前有一个标签,那么在浏览器解析HTML页面的时候,脚本标签内的JS代码就会被执行。如果在加载脚本之前尚未创建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>123456789101112131415161718复制代码类型:[javascript]
12.(unknown):Scripterror
当未捕获的JavaScript错误违反跨源策略跨越域边界时,将发生脚本错误。例如,如果你将你的JavaScript代码托管在CDN上,任何未被捕获的错误(冒泡到window.onerror处理程序中的错误,而不是在try-catch中被捕获的错误)都会被报告为"Scripterror",而不是包含有用的信息。这是一种浏览器安全措施,旨在防止跨域传递数据,否则该域将无法通信。
要获取真实的错误消息,请执行以下操作。
发送Access-Control-Allow-Origin标头
将Access-Control-Allow-Origin标头设置为*表示可以从任何域正确访问资源。你可以根据需要将*替换为您的域:例如,Access-Control-Allow-Origin:www.example.com。但是,处理多个域比较复杂,如果使用CDN可能会出现缓存问题,那么可能不值得花费精力。
以下是一些有关如何在各种环境中设置此标头的示例:
Apache
在将提供JavaScript文件的文件夹中,创建一个具有以下内容的.htaccess文件:
Header add Access-Control-Allow-Origin "*"1复制代码类型:[javascript]
Nginx
将add_header指令添加到提供JavaScript文件的location块中:
location ~ ^/assets/ {
add_header Access-Control-Allow-Origin *;
}123复制代码类型:[javascript]
HAProxy
将以下内容添加到提供JavaScript文件的asset后端:
rspadd Access-Control-Allow-Origin:\ *1复制代码类型:[javascript]
在脚本标签上设置crossorigin=“anonymous”
在你的HTML源代码中,对于您设置了Access-Control-Allow-Origin标头的每个脚本,在script标记上设置crossorigin="anonymous"。在script标记上添加crossorigin属性之前,请确保已验证是否已为脚本文件发送了标头。在Firefox中,如果存在crossorigin属性,但没有Access-Control-Allow-Origin标头,则不会执行脚本。
13.TypeError:Objectdoesn’tsupportproperty
这是在IE中发生的错误,当您调用undefined的方法时,你可以在IE开发人员控制台中对此进行测试。
这等效于Chrome中的错误“TypeError:‘undefined’isnotafunction”。是的,对于相同的逻辑错误,不同的浏览器可能具有不同的错误消息。
这是IE在采用JavaScript命名空间的Web应用程序中常见的问题,在这种情况下,99.9%的问题是IE无法将当前名称空间中的方法绑定到this关键字。
例如,如果你的JS命名空间Rollbar使用isAwesome方法。通常,如果你在Rollbar名称空间中,则可以使用以下语法调用isAwesome方法:
this.isAwesome();1复制代码类型:[javascript]
Chrome,Firefox和Opera将很乐意接受此语法。另一方面,IE则不会。因此,在使用JS命名空间时,最安全的方法是用实际的命名空间作为前缀。
Rollbar.isAwesome();1复制代码类型:[javascript]
14.TypeError:‘undefined’isnotafunction
这是在Chrome中发生的错误,当你调用undefined的函数时。你可以在Chrome开发者控制台和MozillaFirefox开发者控制台中对此进行测试。
这是Chrome浏览器在几种情况下出现的错误,一种是调用不终止的递归函数。你可以在Chrome开发者控制台中对此进行测试。
考虑以下示例代码片段:
function clearBoard(){
alert("Cleared");
}
document.addEventListener("click", function(){
this.clearBoard(); // 这个 "this" 是什么?
});123456复制代码类型:[javascript]
如果执行上述代码,然后单击该页面,则会导致以下错误“UncaughtTypeError:this.clearBoardnotafunction”。原因是正在执行的匿名函数是在文档的上下文中,而clearBoard是在window中定义的。
传统的、与旧浏览器兼容的解决方案是简单地将对它的引用保存在一个变量中,然后闭包可以继承这个变量。例如:
var self = this;
document.addEventListener("click", function(){
self.clearBoard();
});1234复制代码类型:[javascript]
另外,在较新的浏览器中,可以使用bind()方法传递正确的引用:
document.addEventListener("click",this.clearBoard.bind(this));1复制代码类型:[javascript]
15.UncaughtRangeError:Maximumcallstack
这是Chrome浏览器在几种情况下出现的错误,一种是调用不终止的递归函数。你可以在Chrome开发者控制台中对此进行测试。
如果将值传递给超出范围的函数,也可能会发生这种情况。许多函数的输入值仅接受特定范围的数字,例如,Number.toExponential(digits)和Number.toFixed(digits)接受0到20之间的数字,而Number.toFixed(digits)接受1到21之间的数字。
var a = newArray(4294967295); //OK
var b = newArray(-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!1234567891011121314复制代码类型:[javascript]
16.TypeError:Cannotreadproperty‘length’
这是Chrome浏览器中发生的错误,因为读取undefined的变量的length属性,你可以在Chrome开发者控制台中进行测试。
通常情况下,你可以在数组上找到定义的长度,但如果数组没有初始化或者变量名被隐藏在其他上下文中,你可能会遇到这个错误。通过以下示例让我们了解此错误。
var testArray= ["Test"];
function testFunction(testArray) {
for (var i = 0; i < testArray.length; i++) {
console.log(testArray[i]);
}
}
testFunction();1234567复制代码类型:[javascript]
当你声明一个带参数的函数时,这些参数就变成了局部参数。这意味着即使你具有名称为testArray的变量,函数内具有相同名称的参数仍将被视为局部参数。
var testArray = ["Test"];
/* 前置条件:在函数外部定义testArray */
function testFunction(/* No params */) {
for (var i = 0; i < testArray.length; i++) {
console.log(testArray[i]);
}
}
testFunction()12345678910复制代码类型:[javascript]
你可以通过两种方式解决问题:
删除函数声明语句中的参数(事实证明,你想访问那些在函数外部声明的变量,因此你不需要为函数使用参数)
调用函数,将我们声明的数组传递给它。
var testArray = ["Test"];
function testFunction(testArray) {
for (var i = 0; i < testArray.length; i++) {
console.log(testArray[i]);
}
}
testFunction(testArray);123456789复制代码类型:[javascript]
17.UncaughtTypeError:Cannotsetproperty
当我们尝试访问未定义的变量时,它总是返回undefined,我们无法获取或设置任何undefined属性。在这种情况下,应用程序将引发“UncaughtTypeError:Cannotsetproperty”。
例如,在Chrome浏览器中:
如果test对象不存在,则错误将引发“UncaughtTypeError:Cannotsetproperty”。
18.ReferenceError:eventisnotdefined
当您尝试访问undefined或超出当前范围的变量时,将引发此错误。你可以在Chrome浏览器中非常轻松地对其进行测试。
如果你在使用事件处理系统时收到这个错误,请确保你使用传入的事件对象作为参数。IE等较旧的浏览器会提供全局变量事件,而Chrome会自动将事件变量附加到处理程序。Firefox不会自动添加它。jQuery之类的库试图规范这种行为,尽管如此,最好还是使用传递给事件处理程序函数的方法。
document.addEventListener("mousemove", function (event) {
console.log(event);
})
nt);
})