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

如何避免JavaScript开发人员常犯的9个错误?

时间:2023-03-19 14:44:27 科技观察

JavaScript是一种脚本语言,可以为网页添加功能和交互性,对于使用不同编程语言的初学者来说很容易理解。通过一些教程,您可以立即开始使用它。但是很多初学者都会犯一些常见的错误。在本文中,我们将介绍9个常见错误(或不良做法)及其解决方案,以帮助您成为更好的JavaScript开发人员。混淆赋值运算符(=)和等式运算符(==,===)顾名思义,赋值运算符用于给变量赋值。开发人员经常将它与相等运算符混淆。例如:constname="javascript";if((name="nodejs")){console.log(name);}//output-nodejs在这个例子中,不是比较name变量和nodejs字符串,而是name分配nodejs并将nodejs输出到控制台。在JavaScript中,两个等号(==)和三个等号(===)是比较运算符。对于上面的代码,可以比较值使用:constname="javascript";if(name=="nodejs"){console.log(name);}//nooutput//ORif(name=="nodejs"){console.log(name);}//nooutput这两个比较运算符的区别在于:两个等号进行松散比较,三个等号进行严格比较。粗略比较时,只比较数值。但是严格来说,值和数据类型都是要比较的。下面的代码更好地解释了这一点:constnumber="1";console.log(number==1);//trueconsole.log(number===1);//false将1赋值给变量number。如果使用双等号将number与1进行比较,则返回true,因为两个值都是1。但是,在使用三等号的情况下,返回false,因为每个值的数据类型不同。期望回调是同步的在JavaScript中,使用回调方法来处理异步操作。然而,Promises和async/await是处理异步操作的首选方式,因为多个回调会导致回调地狱。回调是异步的。它们在延迟执行完成操作后作为函数被调用。例如,全局setTimeout接收回调函数作为第一个参数,持续时间(以毫秒为单位)作为第二个参数:functioncallback(){console.log("Iamthefirst");}setTimeout(callback,300);console.log("Iamthelast");//output//Iamthelast//Iamthelast在300ms后调用回调函数。但其余代码在完成之前运行,因此最后一个console.log将首先运行。开发人员常犯的一个错误是误解回调是同步的,例如,认为回调函数使用其他操作的值。报错是:functionaddTwoNumbers(){letfirstNumber=5;letsecondNumber;setTimeout(function(){secondNumber=10;},200);console.log(firstNumber+secondNumber);}addTwoNumbers();//NaN不确定是由于secondNumber,所以输出NaN。在运行firstNumber+secondNumber时,secondNumber仍然没有定义,因为setTimeout函数会在200ms后执行回调。最好的方法是执行回调函数中剩余的代码:functionaddTwoNumbers(){letfirstNumber=5;letsecondNumber;setTimeout(function(){secondNumber=10;console.log(firstNumber+secondNumber);},200);}addTwoNumbers();//15this指的是错误在JavaScript中,这是一个经常被误解的概念。要在JavaScript中使用this,您需要了解它的作用,这里与其他语言中this的用法不同。这是一个常见错误的示例:constobj={name:"JavaScript",printName:function(){console.log(this.name);},printNameIn2Secs:function(){setTimeout(function(){console.log(this.name);},2000);},};obj.printName();//JavaScriptobj.printNameIn2Secs();//undefined第一个结果是JavaScript,因为this.name正确指向了名字对象属性。第二个结果是未定义的,因为它没有引用对象的属性(包括名称)。原因是这取决于调用函数的对象。每个函数都有一个this变量,但它的引用由调用this的对象决定。bj.printName()的this直接指向obj。obj.printNameIn2Secs的this直接指向obj。但是,这并不指向回调函数setTimeout中的任何内容,因为没有任何内容调用它。如果对象调用setTimeout,则执行obj.setTimeout...。由于没有可调用此函数的对象,因此使用默认对象(即window)。窗口上没有名称,因此返回未定义。在setTimeout中保留此引用的最佳方法是使用bind、call、apply或箭头函数(在ES6中引入)。与常规函数不同,箭头函数不会创建自己的this。因此,以下代码将保留此引用:constobj={name:"JavaScript",printName:function(){console.log(this.name);},printNameIn2Secs:function(){setTimeout(()=>{console.log(this.name);},2000);},};obj.printName();//JavaScriptobj.printNameIn2Secs();//JavaScript忽略对象的可变性JavaScript对象中的引用数据类型不像字符原始数据类型,例如字符串和数字。比如在一个键值对对象中:constobj1={name:"JavaScript",};constobj2=obj1;obj2.name="programming";console.log(obj1.name);//programmingobj1和obj2指向在内存中相同的地址。数组中:constarr1=[2,3,4];constarr2=arr1;arr2[0]="javascript";console.log(arr1);//['javascript',3,4]开发者常犯错误A错误是忽略了JavaScript的这个特性,这会导致意想不到的错误。如果是这种情况,任何访问原始属性的尝试都将返回undefined或引发错误。最好的方法是每当你想复制一个对象时总是创建一个新的引用。为此,展开运算符(...在ES6中引入)是一个完美的解决方案。例如,在键值对对象中:constobj1={name:"JavaScript",};constobj2={...obj1};console.log(obj2);//{name:'JavaScript'}obj2.name="programming";console.log(obj.name);//'JavaScript'在数组中:constarr1=[2,3,4];constarr2=[...arr1];console.log(arr2);//[2,3,4]arr2[0]="javascript";console.log(arr1);//[2,3,4]将数组和对象保存到浏览器存储在使用JavaScript时,开发者不妨使用localStorage来保存值。然而,一个常见的错误是将数组和对象直接存储在localStorage中。localStorage只接受字符串。JavaScript将对象转为字符串保存,结果是对象保存为[ObjectObject],数组保存为逗号分隔的字符串。例如:constobj={name:"JavaScript"};window.localStorage.setItem("test-object",obj);console.log(window.localStorage.getItem("test-object"));//[ObjectObject]constarr=["JavaScript","programming",45];window.localStorage.setItem("test-array",arr);console.log(window.localStorage.getItem("test-array"));//JavaScript,programming,45保存这些对象时,很难访问它们。例如,对于一个对象,通过.name访问它会导致错误。因为[ObjectObject]现在是一个字符串并且不包含名称属性。通过使用JSON.stringify(将对象转换为字符串)和JSON.parse(将字符串转换为对象)更好地保存本地存储对象和数组。可以通过这种方式轻松访问对象。上面代码的正确版本是:constobj={name:"JavaScript"};window.localStorage.setItem("test-object",JSON.stringify(obj));constobjInStorage=window.localStorage.getItem("test-object");console.log(JSON.parse(objInStorage));//{name:'JavaScript'}constarr=["JavaScript","programming",45];window.localStorage.setItem("test-array",JSON.stringify(arr));constarrInStorage=window.localStorage.getItem("test-array");console.log(JSON.parse(arrInStorage));//JavaScript,编程,45不使用默认值来为动态变量设置默认值是防止意外错误的好方法。下面是一个常见错误的例子:functionaddTwoNumbers(a,b){console.log(a+b);}addTwoNumbers();//NaN由于a未定义,b也未定义,所以结果为NaN。您可以通过使用默认值来防止类似的错误:functionaddTwoNumbers(a,b){if(!a)a=0;if(!b)b=0;console.log(a+b);}addTwoNumbers();//0另外在ES6中可以这样使用默认值:functionaddTwoNumbers(a=0,b=0){console.log(a+b);}addTwoNumbers();//0这个例子虽然小但是强调默认值的重要性。此外,如果未提供预期,开发人员可以提供错误或警告消息。变量命名错误是的,开发人员仍然会犯这个错误。命名很困难,但开发人员别无选择。注解和命名变量一样,是良好的编程习惯。例如:functiontotal(discount,p){returnp*discount}变量discount很好,但是p或total呢?总数是多少?最好是:functiontotalPrice(discount,price){returndiscount*price}适当地命名变量非常重要,因为在某个时间和将来,可能会有其他开发人员使用这个代码库。正确命名变量可以让贡献者更容易理解项目是如何工作的。检查布尔值constisRaining=falseif(isRaining){console.log('Itisraining')}else{console.log('Itisnotraining')}//Itisnotraining在上面的例子中是检查布尔值的常用方法,但是在测试某些值时仍然出错。在JavaScript中,比较0和false返回true,比较1和true返回true。也就是说,如果isRaining为1,则为真。这往往是对象的错误,如:constobj={name:'JavaScript',number:0}if(obj.number){console.log('numberpropertyexists')}else{console.log('numberpropertydoesnotexist')}//numberpropertydoesnotexistobj.number返回0尽管存在number属性,这是一个false值,因此执行else代码。所以,除非你知道要使用的值的范围,否则你应该测试对象中的布尔值和属性:if(a===false)...if(object.hasOwnProperty(property))...makes容易混淆的加法和连接在JavaScript中,加号(+)有两个功能:加法和连接。加法用于数字,连接用于字符串。一些开发人员经常滥用此运算符。例如:constnum1=30;constnum2="20";constnum3=30;constword1="Java"constword2="Script"console.log(num1+num2);//3020console.log(num1+num3);//60console.log(word1+word2);//当JavaScript添加一个字符串和一个数字时,JavaScript将数字转换为字符串。添加数字时,将执行数学运算。总结除了上面列举的,肯定还有更多的错误(小错误或者大错误)。所以,你需要了解最新的语言发展。学习并避免这些错误将帮助您构建更好、更可靠的Web应用程序和工具。