JavaScript常涌现的毛病前十位
为了可读性,毛病称号举行了肯定的简写。让我们深切相识每一个毛病发作的缘由以及处置惩罚要领。
1. Uncaught TypeError: Cannot Read Property
假如你是一位JavaScript开辟人员,你可以已记不清楚多少次看到这个毛病了。当你读取一个undefined对象的属性或是挪用其上的要领时,就会涌现这个毛病。你可以再Chrome Console中举行测试。
致使这个题目的缘由有许多,最常见的是衬着UI组件时对state不适当的初始化。让我们看一个实在APP中可以涌现该状况的例子。我们挑选了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>
);
}
}
这里要注意两件主要的事变:
- 组件的state(比方 this.state)在生命周期开始时为undefined。
- 当你异步猎取数据的时刻,component会在数据加载之前最少衬着一次 – 不管是不是在constructor中猎取数据,都邑运转
componentWillMount
或是componentDidMount
。当Quiz
第一次衬着的时刻,this.state.items
为undefined。因而,item列表取得的值为undefined,因而会报错"Uncaught TypeError: Cannot read property ‘map’ of undefined"
。
这个题目很轻易处置惩罚。最简朴的要领是,在组织器内里将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>
);
}
}
这和你的项目中的代码不肯定完全雷同,然则我们愿望给你供应一个处置惩罚或是防备该题目的思绪。
2. TypeError: ‘undefined’ Is Not an Object (evaluating…)
这是一个在Safari中在undefined对象上接见属性或要领时报的错。你可以在Safari的掌握台上举行测试。这个毛病和之前在Chrome中涌现的毛病是雷同,只是报错信息差别。
3. TypeError: Null Is Not an Object (evaluating…)
这是在Safari中在接见null对象上的属性或要领时报的错。
风趣的是,在JavaScript中,null和undefined是差别的,所以我们看到了两个差别的报错信息。Undefined一般是指一个还没有赋值的变量,而null是指该变量的值为空。要想推断两者不等,应该运用严厉的相称操作符:
在实在天下中,这类毛病可以涌现的缘由之一是你试图在元素加载完成之前接见DOM元素。关于空缺的对象援用,DOM API会返回null。
任何对DOM元素举行处置惩罚的JS代码都应该都在DOM元素建立完成以后举行。JS代码根据HTML中的划定按从上到下的递次举行诠释。所以,假如在DOM元素之前存在标签,则剧本标签内的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上,任何未被捕获的毛病(没有被try-catch块捕获,被冒泡至window.onerror处置惩罚器的毛病)将会被简朴的报告为Script Error
,不包括任何有效的信息。这是浏览器的一种平安措施,旨在防备跨域通报数据。
要想取得真正的报错信息,做以下几步:
1. 发送Access-Control-Allow-Origin头
将Access-Control-Allow-Origin
设置为.
来标记该资本从任何域都可以一般接见。假如需要的话,也可以将其设置为本身的域名:比方,Access-Control-Allow-Origin: www.example.com
。然则,处置惩罚多个域会变的辣手,而且假如你是出于缓存的题目而运用CDN,那末如许子的价值可以不值得。概况参考这里
这里给出一些在差别的环境中设置header的例子:
Apache
在你寄存JavaScript的文件夹中增添一个.htacess文件,包括以下内容:
Header add Access-Control-Allow-Origin "*"
Nginx
将add_header指令增添到为JavaScript文件供应效劳的位置块:
location ~ ^/assets/ {
add_header Access-Control-Allow-Origin *;
}
HAProxy
将以下内容增添到供应JavaScript的asset backend
rspadd Access-Control-Allow-Origin:\ *
2. 在script标签上设置crossorigin="annonymous"
属性
在HTML中,关于每一个设置了Access-Control-Allow-Origin
头的剧本,在剧本的标签上增添crossorigin="anonymous"
属性。在将crossorigin
属性增添到剧本之前,请确保考证是不是为剧本文件设置了header。在火狐浏览器中,假如设置了crossorigin
属性然则没有设置Access-Control-Allow-Origin
头,该剧本不会实行。
5. TypeError: Object Doesn’t Support Property
这是在IE浏览器中报的错,当你试图挪用一个undefined对象的要领时:
这等价于Chrome中的TypeError: ‘undefined’ is not a function
毛病。是的,差别的浏览器对雷同的毛病会发生差别的报错信息。
关于运用JavaScript定名空间的Web顺序,在IE上运转时经常会碰到这个毛病。当这个毛病涌现时,99.9%的状况是因为IE不能将当前的定名空间的要领绑定到this
关键字上。比方,假定你有一个JS定名空间Rollbar
,其下有一个要领isAwesome()
。一般在Rollbar
定名空间下你会用以下的语法挪用isAwesome
要领:
this.isAwesome();
Chrome,Firfox和Opera都邑兴奋的接收这个语法。然则,IE并不会。因而,运用JS定名空间时最平安的挑选是一直以现实的定名空间作为前缀。
Rollbar.isAwesome();
6. TypeError: ‘undefined’ Is Not a Function
这是当你在Chrome中试图挪用undefined的要领时涌现的毛病。
跟着JavaScript的编程技能和设想形式在这几年来愈来愈庞杂,在回折衷闭包中自我援用局限的散布也响应的增添,致使对this
涌现疑心。
看下面这段代码:
function testFunction() {
this.clearLocalStorage();
this.timer = setTimeout(function() {
this.clearBoard(); // what is "this"?
}, 0);
};
运转上面的代码会涌现"Uncaught TypeError: undefined is not a function."
报错。缘由是当你试图挪用setTimeout()
要领时,你现实上在挪用window.setTimeout()
要领。因而,一个匿名的函数传入到setTimeout()
要领中,该函数的高低文现实上是window对象,而window对象没有clearBoard()要领。
一个传统的,浏览器兼容的计划是将援用this
存储到一个变量中,该援用可以被闭包继续,以下:
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 reset(){
this.clearBoard(); //back in the context of the right 'this'!
};
7. Uncaught RangeError: Maximum Call Stack
这是在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!
8. TypeError: Cannot Read Property ‘length’
这是在Chrome中读取一个undefined对象的length属性时报的错。
你一般可以在array中找到length属性,然则你也可以在array还没有初始化或是变量名被隐藏在另一个高低文中时碰到这个毛病。让我们用下面这个例子明白一下这个报错:
var testArray= ["Test"];
function testFunction(testArray) {
for (var i = 0; i < testArray.length; i++) {
console.log(testArray[i]);
}
}
testFunction();
当你在要领中声明参数时,这些参数成为了局部变量。这意味着纵然你有名为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();
- 向要领传入声明的参数
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
,而我们不能猎取或是设置undefined的属性。这时刻,运用就会抛出“Uncaught TypeError cannot set property of undefined.”
报错。
假如test
对象不存在,也会抛出“Uncaught TypeError cannot set property of undefined.”
。
10. ReferenceError: Event Is Not Defined
当你试图接见的变量为undifined或是不在当前作用域局限内时,会抛出这个毛病:
假如你在运用事宜处置惩罚体系时碰到这个报错,请确保你将事宜对象作为参数传入了处置惩罚要领中。老的浏览器器如IE会供应一个全局的事宜变量,而Chrome会自动将事宜变量隶属到handler上。Firfox不会自动增添它。而相似jQuery之类的库则试图规范化这个行动。总之,你最好将event作为采用数传入事宜处置惩罚要领中:
document.addEventListener("mousemove", function (event) {
console.log(event);
})
总结
看来大多数的毛病都是null或是undefined相干的毛病。假如你在运用编译器的严厉形式选项,一个优越的范例搜检体系如Typescript
可以协助你防备这些题目。它会在一个预期范例没有被定义时正告你。即使没有Typescript, 它也能协助我们运用防御性编程,在挪用对象之前搜检对象是不是是undefined。
我们愿望你可以学到一些新的内容,而且在将来可以防备这些毛病,也可以这个指南帮你处置惩罚了一些头疼的题目。不管如何,即使是最好实践,在编码过程当中照样会涌现意料之外的毛病。相识影响用户运用的毛病而且具有可以疾速处置惩罚题目的东西是很主要的。
想要相识更多开辟手艺,口试教程以及互联网公司内推,迎接关注我的微信民众号!将会不定期的发放福利哦~