发布时间:2022-7-14 分类: 电商动态
如今,代购源码网站几乎使用了100%的JavaScript。 JavaScript似乎是一种非常简单的语言,但事实并非如此。它有很多容易被误解的细节。如果你不注意,它将导致错误。
1. 错误的对this进行引用
在闭包或回调中,this关键字的范围很容易出错。例如:
Game.prototype.restart=function(){
this.clearLocalStorage();
This.timer=setTimeout(function(){
this.clearBoard(); //这指的是哪里?
},0);
};
如果您执行上面的代码,我们会看到一个错误:
未捕获的TypeError: undefined不是函数
出错的原因是当你调用setTimeout函数时,实际上是在调用window.setTimeout()。在setTimeout中传递的匿名函数位于窗口环境中,因此它指向窗口,但窗口没有clearBoard方法。
怎么解决?定义一个新的变量引用,指向Game对象的this对象,然后就可以使用它了。
Game.prototype.restart=function(){
this.clearLocalStorage();
Var self=this; //将此指向的对象绑定到self
This.timer=setTimeout(function(){
self.clearBoard();
},0);
};
或者使用bind()函数:
Game.prototype.restart=function(){
this.clearLocalStorage();
This.timer=setTimeout(this.reset.bind(this),0); //绑定到'this'
};
Game.prototype.reset=function(){
this.clearBoard(); //这里的引用是正确的
};
2. 和块作用域(block scope)有关的BUG
在大多数编程语言中,每个功能块都有一个单独的新范围,但不是在JavaScript中。例如:
对于(var i=0; i< 10; i ++){
/* ... */
}
CONSOLE.LOG(ⅰ); //会输出什么?
通常在这种情况下,调用console.log()将输出undefined或错误。但是,这里将有10个输出。在JavaScript中,即使for循环结束,变量i仍然存在并且记录最后一个值。一些开发人员会忘记这一点,然后导致许多错误。我们可以使用let代替for来消除这个问题。
3. 内存泄漏
您需要监视内存使用情况,因为很难避免泄漏。内存泄漏可能是由对不存在的对象或循环引用的引用引起的。
如何避免:关注对象的可达性。
可访问的对象:
可以在现有调用堆栈中的任何位置访问的对象
全局对象
当可以通过引用访问对象时,它将保存在内存中。浏览器的垃圾收集器仅回收那些不可访问的对象。
4. 混淆的相等判断
JavaScript会自动将布尔环境中的所有变量类型转换为布尔类型,但可能会导致错误。例如:
//都是真的
Console.log(false=='0');
Console.log(null==undefined);
CONSOLE.LOG(”'==0);
Console.log(''==0);
//注意:以下两个也是
如果({})//…
如果([])//…
{}和[]都是对象,它们都被转换为true。为了防止错误,建议使用===和!==进行比较,因为类型转换不是隐式完成的。
5. 低效的DOM操作
在JavaScript中,您可以轻松地操作DOM(添加,修改和删除),但开发人员往往操作效率低下。这可能导致错误,因为这些操作计算量很大。要解决此问题,如果需要操作多个DOM元素,建议使用Document Fragment。
广告:你的在线代码真的没有BUG吗?欢迎免费使用Fundebug!我们可以帮您找到错误!
6. 在for循环中错误的定义函数
例如:
Var elements=document.getElementsByTagName('input');
Var n=elements.length; //假设我们有10个元素
对于(var i=0; i< n; i ++){
Elements [i] .onclick=function(){
Console.log('元素编号'+ i);
};
}
如果我们有10个元素,点击任何元素将显示“ldquo;元素编号10”!因为在调用onclick时for循环已经结束,所以我都是10.
解决方案:
Var elements=document.getElementsByTagName('input');
Var n=elements.length; //假设有10个元素
Var makeHandler=function(num){//外部函数
返回函数(){//内部函数
Console.log('元素编号'+ num);
};
};
对于(var i=0; i< n; i ++){
Elements [i] .onclick=makeHandler(i + 1);
}
执行for循环时立即调用makeHandler,获取当前值i + 1并存储在变量num中。 makeHandler返回一个使用num变量的函数,该变量绑定到元素的click事件。
7. 通过原型错误地继承
如果开发人员无法正确理解继承的原则,那么就可以编写带有错误的代码:
BaseObject=function(name){
如果(typeof name!=='undefined'){
This.name=name;
} else {
This.name='default'
}
};
Var firstObj=new BaseObject();
Var secondObj=new BaseObject('unique');
CONSOLE.LOG(firstObj.name); // - >输出'默认'
CONSOLE.LOG(secondObj.name); // - >输出'独特'
但是,如果我们执行以下操作:
删除secondObj.name;
然后:
CONSOLE.LOG(secondObj.name); // - >输出'undefined'
我们真正想要的结果是打印默认名称。
BaseObject=function(name){
如果(typeof name!=='undefined'){
This.name=name;
}
};
BaseObject.prototype.name='default';
每个BaseObject都继承name属性,默认值为default。此时,如果删除了secondObj的name属性,则原型链接将返回正确的默认值。
Var thirdObj=new BaseObject('unique');
CONSOLE.LOG(thirdObj.name); // - >输出'独特'
删除thirdObj.name;
CONSOLE.LOG(thirdObj.name); // - >输出'默认'
8. 实例方法中的无效引用
让我们实现一个简单的构造函数来创建一个对象:
Var MyObject=function(){}
MyObject.prototype.whoAmI=function(){
Console.log(这==window?'window':'MyObj');
};
Var obj=new MyObject();
为了便于使用,我们将变量whoAmI定义为引用obj.whoAmI:
Var whoAmI=obj.whoAmI;
打印出来看看:
CONSOLE.LOG(WHOAMI);
控制台将输出:
函数(){
Console.log(这==window?'window':'MyObj');
}
现在让我们比较两个调用之间的差异:
obj.whoAmI(); //输出'MyObj'(符合预期)
我是谁(); //输出'window'(实际输出窗口)
当我们将obj.whoAmI分配给whoAmI时,新变量whoAmI是全局定义的,所以这指向全局窗口,而不是MyObj。如果我们真的想要获得对MyObj函数的引用,我们需要在其范围之内。
Var MyObject=function(){}
MyObject.prototype.whoAmI=function(){
Console.log(这==window?'window':'MyObj');
};
Var obj=new MyObject();
Obj.w=obj.whoAmI; //仍然在obj的范围内
obj.whoAmI(); //输出'MyObj'
Obj.w(); //输出'MyObj'
9. setTimeout/setInterval函数第一个参数误用字符串
如果将字符串作为setTimeout/setTimeInterval,它将被传递给函数构造函数,并将构建一个新函数。这个过程缓慢而且效率低下并导致错误。
Var hello=function(){
Console.log('hello,fundebug!');
}
setTimeout('hello',1000);
一个很好的选择是将函数作为参数传递:
setInterval(logTime,1000); //将logTime函数传递给
setTimeout(function(){//传入一个匿名函数
的LogMessage(msgValue);
},1000);
10. 未能成功使用strict mode
使用严格模型会增加许多限制,以增强安全性并防止发生某些错误。如果您不使用严格模式,那么您就相当于拥有一个强大的帮助器来帮助您避免错误:
更容易调试
避免意外定义不应定义的全局变量
避免这种隐式转换
避免重用属性名称或参数值
Eval()更安全
无效的删除使用会自动抛出错误
版权声明:
转载时请注明作者的Fundebug和本文的地址:
https://blog.fundebug.com/2017/11/15/top_10_bugs_and_fixing_method/