
10个JavaScript普遍BUG及修补方式
2021-01-20 10:32
现如今网站基本上100%应用JavaScript。JavaScript看上去是1门10分简易的語言,但是客观事实其实不这般。它有许多非常容易被弄错的细节,1不留意就致使BUG。
1. 不正确的对this开展引入
在闭包或则回调函数中, this 重要字的功效域很非常容易弄错。举个事例:
Game.prototype.restart = function () {
this.clearLocalStorage();
this.timer = setTimeout(function() {
this.clearBoard(); // 此处this指的是?
}, 0);
};
假如实行上面的编码,大家会看到出错:
Uncaught TypeError: undefined is not a function
错误的缘故在于:当你启用 setTimeout 涵数,你具体上启用的是 window.setTimeout() 。在 setTimeout 中传入的密名涵数是在 window 这个目标自然环境下,因此 this 是指向 window ,可是 window 并沒有 clearBoard 方式。
怎样处理呢?界定新的自变量引入指向 Game 目标的 this ,随后便可以应用啦。
Game.prototype.restart = function () {
this.clearLocalStorage();
var self = this; // 将this指向的目标关联到self
this.timer = setTimeout(function(){
self.clearBoard();
}, 0);
};
或则应用 bind() 涵数:
Game.prototype.restart = function () {
this.clearLocalStorage();
this.timer = setTimeout(this.reset.bind(this), 0); // bind to 'this'
};
Game.prototype.reset = function(){
this.clearBoard(); // 此处this的引入正确
};
2. 和块功效域(block scope)相关的BUG
在大多数数程序流程語言中,每个涵数块都有1个单独的新的功效域,可是在JavaScript中其实不是。比如:
for (var i = 0; i i++) {
/* ... */
}
console.log(i); // 会輸出甚么呢?
一般在这类状况下,启用 console.log() 会輸出 undefined 或则出错。但是呢,这里会輸出 10 。在JavaScript中,即便for循环系统早已完毕,自变量 i 仍然存在,而且纪录最终的值。一些开发设计者会忘掉这1点,随后致使很多bug。大家可使用 let 而并不是 for 来避免这1难题。
3. 运行内存泄露
你必须监管运行内存应用量,由于泄漏很难防止。运行内存泄漏将会因为引入不存在的目标或则循环系统引入致使。
怎样防止:关心目标的可浏览性(reachability)。
可浏览的目标:
现有的call stack任何部位能够浏览的目标
全局性目标
当1个目标能够根据引入浏览到,那末会在运行内存中储存。访问器的废弃物收购器仅仅会把那些不能浏览的目标收购。
4. 搞混的相同分辨
JavaScript全自动将全部在布尔运算自然环境下的自变量种类变换为布尔运算种类,可是将会致使bug。举例:
// 全部全是true
console.log(false == '0');
console.log(null == undefined);
console.log(" \t\r\n" == 0);
console.log('' == 0);
// 留意:下面两个也是
if ({}) //
if ([]) //
{} 和 [] 全是目标,她们都会被变换为true。以便避免bug出現,强烈推荐应用 === 和 !== 来做较为,由于不容易隐式做种类变换。
5. 低效的DOM实际操作
在JavaScript中,你能够轻轻松松实际操作DOM(加上、改动和删掉),可是开发设计者常常很低效地去实际操作。这会致使bug出現,由于这些实际操作十分消耗测算資源。以便处理这个难题,强烈推荐应用文本文档碎片(Document Fragment),假如你必须实际操作好几个DOM元素。
广告宣传: 你的网上编码真的沒有BUG吗?欢迎完全免费应用 Fundebug !大家能够协助您第1時间发现BUG!
6. 在for循环系统中不正确的界定涵数
举例:
var elements = document.getElementsByTagName('input');
var n = elements.length; // 假定大家有10个元素
for (var i = 0; i i++) {
elements[i].onclick = function() {
console.log("元素序号#" + i);
};
}
假如大家有10个元素,那末点一下任何1个元素都会显示信息 元素序号#10 !由于在 onclick 被启用的情况下,for循环系统早已完毕,因而全部的i全是10。
解法:
var elements = document.getElementsByTagName('input');
var n = elements.length; // 假定有10个元素
var makeHandler = function(num) { // outer function
return function() { // inner function
console.log("元素序号##" + num);
};
};
for (var i = 0; i i++) {
elements[i].onclick = makeHandler(i+1);
}
makeHandler 在for循环系统实行的情况下马上被启用,获得到当今的值 i+1 ,而且储存在自变量 num中。 makeHandler 回到1个涵数应用 num 自变量,该涵数被关联到元素的点一下恶性事件。
7. 根据原形不正确地承继
开发设计者假如没能正确了解承继的基本原理,那末便可能写出有bug的编码:
BaseObject = function(name) {
if(typeof name !== "undefined") {
this.name = name;
} else {
this.name = 'default'
}
};
var firstObj = new BaseObject();
var secondObj = new BaseObject('unique');
console.log(firstObj.name); // - 輸出'default'
console.log(secondObj.name); // - 輸出'unique'
可是,假如大家做以下实际操作:
delete secondObj.name;
那末:
console.log(secondObj.name); // - 輸出'undefined'
而大家具体上要想的結果是复印默认设置的name。
BaseObject = function (name) {
if(typeof name !== "undefined") {
this.name = name;
}
};
BaseObject.prototype.name = 'default';
每个 BaseObject 都承继 name 特性,而且默认设置值为 default 。此时假如 secondObj 的 name 特性被删掉掉,根据原形链搜索会回到正确的默认设置值。
var thirdObj = new BaseObject('unique');
console.log(thirdObj.name); // - 輸出'unique'
delete thirdObj.name;
console.log(thirdObj.name); // - 輸出'default'
8. 案例方式中的失效引入
大家来完成1个简易的结构涵数用来建立目标:
var MyObject = function() {}
MyObject.prototype.whoAmI = function() {
console.log(this === window ? "window" : "MyObj");
};
var obj = new MyObject();
以便应用便捷,大家界定自变量 whoAmI 来引入 obj.whoAmI :
var whoAmI = obj.whoAmI;
复印出看来看:
console.log(whoAmI);
操纵台会輸出:
function () {
console.log(this === window ? "window" : "MyObj");
}
如今大家来比照1下二者启用的差别:
obj.whoAmI(); // 輸出"MyObj" (和期待1致)
whoAmI(); // 輸出"window" (居然輸出了window)
当大家把 obj.whoAmI 取值给 whoAmI 的情况下,这个新的自变量 whoAmI 是界定在全局性下,因而 this 指向全局性的 window ,而并不是 MyObj 。假如大家真的要获得对 MyObj 的涵数的引入,必须在其功效域下。
var MyObject = function() {}
MyObject.prototype.whoAmI = function() {
console.log(this === window ? "window" : "MyObj");
};
var obj = new MyObject();
obj.w = obj.whoAmI; // 任然在obj的功效域
obj.whoAmI(); // 輸出"MyObj"
obj.w(); // 輸出"MyObj"
9. setTimeout/setInterval涵数第1个主要参数误用标识符串
假如你将1个标识符串做为setTimeout/setTimeInterval,它会被发送给涵数结构涵数并搭建1个新的涵数。该实际操作步骤很慢并且低效,并致使bug出現。
var hello = function(){
console.log("hello, fundebug !");
}
setTimeout("hello", 1000);
1个好的取代方式便是传入涵数做为主要参数:
setInterval(logTime, 1000); // 将logTime涵数传入
setTimeout(function() { // 传入1个密名涵数
logMessage(msgValue);
}, 1000);
10. 未能取得成功应用strict mode
应用strict model会提升许多限定标准来提升安全性和避免一些不正确的出現,假如不应用 strict mode ,你就非常于少了1个得力的小助手帮你防止不正确:
更为非常容易debug
防止一不小心界定了不应该界定的全局性自变量
防止this隐式变换
防止特性姓名或则主要参数值的反复应用
eval()更为安全性
失效地应用delete会全自动抛错误误
版权申明:
转载时请注明作者Fundebug和本文详细地址:
扫描二维码分享到微信