当前位置: 首页 > 科技观察

10个JavaScript常见BUG及修复方法

时间:2023-03-12 12:03:14 科技观察

10CommonJavaScriptBugsandHowtoFixthem原文:Top10bugsandtheirbugfixing译者:Fundebug为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,转载仅供学习。今天的网站几乎100%使用JavaScript。JavaScript似乎是一门非常简单的语言,其实不然。它有很多容易出错的细节,稍不注意就会导致BUG。1.对this的引用不正确在闭包或回调中,this关键字的范围很容易弄错。例如:Game.prototype.restart=function(){this.clearLocalStorage();this.timer=setTimeout(function(){this.clearBoard();//这里指的是什么?},0);};如果我们执行上面的代码,我们会看到一个错误:UncaughtTypeError:undefinedisnotafunction错误的原因是:当你调用setTimeout函数时,实际上调用的是window.setTimeout()。setTimeout传入的匿名函数是在window对象环境下,所以this指向window,但是window没有clearBoard方法。如何解决?定义一个新的变量引用指向Game对象的this,然后就可以使用了。Game.prototype.restart=function(){this.clearLocalStorage();变种自我=这个;//将this指向的对象绑定到selfthis.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();//这里对this的引用是正确的};2.大部分编程语言都存在与块作用域相关的bug在中,每个功能块都有一个单独的新作用域,而在JavaScript中则没有。例如:for(vari=0;i<10;i++){/*...*/}console.log(i);//会输出什么?通常这种情况下调用console.log()会输出undefined或者报错。但是这里会输出10。在JavaScript中,即使for循环已经结束,变量i仍然存在并记录最后一个值。一些开发人员忘记了这一点并导致许多错误。我们可以通过使用let而不是for来消除这个问题。3.内存泄露你需要监控内存的使用情况,因为内存泄露是很难避免的。内存泄漏可能是由引用不存在的对象或循环引用引起的。如何避免:关注对象的可达性。Accessibleobjects:在现有调用栈的任何地方都可以访问的对象Globalobjects当一个对象可以通过引用访问时,它会被存储在内存中。浏览器的垃圾收集器只会回收不可访问的对象。4.混淆相等判断JavaScript会自动将布尔环境中的所有变量类型转换为布尔类型,但这可能会导致错误。示例://全部为真console.log(false=='0');console.log(null==undefined);console.log("\t\r\n"==0);console.log(''==0);//注意:下面两个也是if({})//...if([])//...{}和[]都是对象,会进行转换为真。为了防止bug,推荐使用===和!==进行比较,因为不会有隐式类型转换。5.低效的DOM操作在JavaScript中,您可以轻松地操作DOM(添加、修改和删除),但开发人员通常效率很低。这可能会导致错误,因为这些操作在计算上非常昂贵。为了解决这个问题,如果需要操作多个DOM元素,推荐使用DocumentFragment。广告:您的在线代码真的没有错误吗?欢迎免费使用Fundebug!我们可以在第一时间帮您发现bug!6.for循环中函数定义错误的例子:varelements=document.getElementsByTagName('input');varn=elements.length;//假设我们有10个元素for(vari=0;i输出'default'console.log(secondObj.name);//->输出'unique'但如果我们这样做:deletesecondObj.name;然后:console.log(secondObj.name);//->输出'undefined'而我们真正想要的是打印默认名称。BaseObject=function(name){if(typeofname!=="undefined"){this.name=name;}};BaseObject.prototype.name='default';每个BaseObject都继承name属性,默认值为default。此时如果删除了secondObj的name属性,通过原型链查找会返回正确的默认值。varthirdObj=newBaseObject('唯一');console.log(thirdObj.name);//->输出'unique'deletethirdObj.name;console.log(thirdObj.name);//->output'default'8.实例方法中的无效引用让我们实现一个简单的构造函数来创建对象:varMyObject=function(){}MyObject.prototype.whoAmI=function(){console.log(this===窗口?"窗口":"MyObj");};varobj=newMyObject();为了方便起见,我们定义变量whoAmI来引用obj.whoAmI:varwhoAmI=obj.whoAmI;打印出来:console.log(whoAmI);控制台会输出:function(){console.log(this===window?"window":"MyObj");}现在让我们比较一下这两个调用的区别:obj.whoAmI();//输出“MyObj”(符合预期)whoAmI();//输出“window”(偶数输出窗口)当我们将obj.whoAmI赋值给whoAmI时,新变量whoAmI是全局定义的,所以this指向的是Globalwindow,而不是MyObj。如果我们真的想获得对MyObj函数的引用,我们需要在它的范围内。varMyObject=function(){}MyObject.prototype.whoAmI=function(){console.log(this===window?"window":"MyObj");};varobj=newMyObject();obj.w=obj.whoAmI;//还在obj的范围内obj.whoAmI();//输出"MyObj"obj.w();//Output"MyObj"9.setTimeout/setInterval函数第一个参数的误用String如果你传递一个字符串作为setTimeout/setTimeInterval,它会被传递给函数构造函数,并构造一个新的函数。此操作过程缓慢且效率低下,并会导致错误。varhello=function(){console.log("hello,fundebug!");}setTimeout("hello",1000);一个好的替代方法是传入一个函数作为参数:setInterval(logTime,1000);//将logTime函数传入setTimeout(function(){//传入一个匿名函数logMessage(msgValue);},1000);10.无法使用严格模式使用严格模式会增加很多限制来加强安全性,防止某些错误,如果你不使用严格模式,你将失去一个帮你避免错误的得力助手:更容易调试避免意外定义不该定义的全局变量避免这种隐式转换避免属性名或参数值重复使用eval()更安全无效使用delete会自动抛错版权声明:转载请注明作者Fundebug及地址本文转载时:https://blog.fundebug.com/2017/11/15/top_10_bugs_and_fixing_method/