好的代码不仅是可以运行的代码,而且是可以被其他人轻松阅读、重用和重构的代码,因为代码除了实现功能外,大部分时候都是两者都是由您或团队的其他成员维护。虽然本文主要关注编写干净的JavaScriptES6代码,与任何框架无关,但下面将提到的大多数示例也适用于其他语言。此外,以下示例也主要来自RobertC.Martin的CleanCode一书中采用的建议,并不意味着要严格遵守。为变量使用有意义的名称变量命名应该是描述性的并且应该是有意义的。经验法则是大多数JavaScript变量应该使用驼峰命名法。//错误?constfoo="JDoe@example.com";constbar="John";constage=23;constqux=true;//OK?constemail="John@example.com";constfirstName="John";constage=23;constisActive=true注意布尔变量名通常看起来是回答问题,例如:isActivedidSubscribehasLinkedAccountAvoidaddingunnecessarycontextDundantcontextisnotrequiredinaspecificobjectorclass//Error?constuser={userId:"296e2589-7b33-400a-b762-007b730c8e6d",userEmail:"JDoe@example.com",userFirstName:"John",userLastName:"Doe",userAge:23,};user.userId;//正确?constuser={id:"296e2589-7b33-400a-b762-007b730c8e6d",email:"JDoe@example.com",firstName:"John",lastName:"Doe",age:23,};user.id;避免硬编码,保证有意义和可搜索的常量声明,而不是直接使用常量值,全局变量建议使用蛇形命名(SCREAMING_SNAKE_CASE)//Error?setTimeout(clearSessionData,900000);//正确?constSESSION_DURATION_MS=15*60*1000;setTimeout(clearSessionData,SESSION_DURATION_MS);该函数使用描述性名称。函数名可以很长,长到足以描述它的功能,一般函数都包含动词来描述它的作用,但返回布尔值的函数是例外,一般以“是”或“否”的形式问题,函数名也应该是驼峰命名。//错?functiontoggle(){//...}functionagreed(user){//...}//正确?functiontoggleThemeSwitcher(){//...}functiondidAgreeToAllTerms(user){//...}直接使用默认值使用默认参数,比短路语法或者在函数中加入判断语句更简洁。值得注意的是,短路语法适用于所有被认为是false的值,例如false、null、undefined、''、""、0和NaN,而默认参数只是简单地替换undefined。//错误?functionprintAllFilesInDirectory(dir){constdirectory=dir||“./”;//...}//OK?functionprintAllFilesInDirectory(dir="./"){//...}限制参数数量有争议。函数的参数不能超过2,表示函数的参数为??0、1或2。如果需要第三个参数,则表示:函数需要拆分,可以将相关参数聚合成Objectdelivery//错误?functionsendPushNotification(title,message,image,isSilent,delayMs){//...}sendPushNotification("NewMessage","...","http://...",false,1000);//正确?functionsendPushNotification({title,message,image,isSilent,delayMs}){//...}constnotificationConfig={title:"NewMessage",message:"...",image:"http://...",isSilent:false,delayMs:1000,};sendPushNotification(notificationConfig);不要在一个函数中做太多事情原则上,一个函数只做一件事,这个原则可以很好的帮助我们减少函数的大小和复杂度,同时也可以更好的进行测试、调试和重构。函数的代码行数是判断函数是否做了太多事情的一个指标。一般建议一个函数的长度在20~30行以内。//错误?functionpingUsers(users){users.forEach((user)=>{constuserRecord=database.lookup(user);if(!userRecord.isActive()){ping(user);}});}//正确?functionpingInactiveUsers(users){users.filter(!isUserActive).forEach(ping);}functionisUserActive(user){constuserRecord=database.lookup(user);returnuserRecord.isActive();}避免使用flag变量flag变量意味着函数可以进一步简化//err?functioncreateFile(name,isPublic){if(isPublic){fs.create(`./public/${name}`);}else{fs.create(name);}}//正确?functioncreateFile(name){fs.create(name);}functioncreatePublicFile(name){createFile(`./public/${name}`);}不要重复自己(干)重复代码不是一个好信号,如果你复制粘贴N次,下次修改这部分代码,你就得同时修改N处。//错误?functionrenderCarsList(cars){car??s.forEach((car)=>{constprice=car.getPrice();constmake=car.getMake();constbrand=car.getBrand();constnbOfDoors=car.getNbOfDoors();render({price,make,brand,nbOfDoors});});}functionrenderMotorcyclesList(motorcycles){motorcycles.forEach((motorcycle)=>{constprice=motorcycle.getPrice();constmake复制代码=motorcycle.getMake();constbrand=motorcycle.getBrand();constseatHeight=motorcycle.getSeatHeight();render({price,make,brand,seatHeight});});}//正确?functionrenderVehiclesList(车辆){vehicles.forEach((vehicle)=>{constprice=vehicle.getPrice();constmake=vehicle.getMake();constbrand=vehicle.getBrand();constdata={price,make,brand};switch(vehicle.type){case"car":data.nbOfDoors=vehicle.getNbOfDoors();break;案例"motorcycle":data.seatHeight=vehicle.getSeatHeight();休息;}渲染(数据);});}避免副作用在JavaScript中,首选模式应该是函数式而不是命令式,换句话说,要保持函数的纯粹性,副作用可以修改共享的状态和资源,会导致代码不稳定和难以测试、排查问题会特别困难。所有的副作用都应该集中管理。如果需要修改全局状态,可以定义一个统一的服务来修改。//错误?letdate="21-8-2021";functionsplitIntoDayMonthYear(){date=date.split("-");}splitIntoDayMonthYear();//另一个函数可能期望日期作为字符串console.log(日期);//['21','8','2021'];//正确?functionsplitIntoDayMonthYear(date){returndate.split("-");}constdate="21-8-2021";constnewDate=splitIntoDayMonthYear(date);//原始值是完整的console.log(date);//'21-8-2021';console.log(newDate);//['21','8','2021'];另外,如果一个变量对象作为函数的参数传入,返回这个参数时,应该是这个参数的克隆对象,而不是直接修改对象返回。//错误?functionenrollStudentInCourse(course,student){course.push({student,enrollmentDate:Date.now()});}//正确?functionenrollStudentInCourse(course,student){return[...course,{student,enrollmentDate:Date.now()}];}并发避免使用回调回调函数太乱了,所以ES6为我们提供了Promise,允许我们使用链式回调。当然,Async/Await提供了更简洁的解决方案,可以让我们写出更线性的代码,account){getReports(account,function(err,reports){sendStatistics(reports,function(err){console.error(err);});});});});});//正确?getUser().then(getProfile).then(getAccount).then(getReports).then(sendStatistics).catch((err)=>console.error(err));//正确的??异步函数sendUserStatistics(){尝试{constuser=awaitgetUser();constprofile=awaitgetProfile(用户);constaccount=awaitgetAccount(个人资料);常量报告=等待getReports(账户);返回发送统计(报告);}catch(e){console.error(err);}}错误处理处理抛出的异常和被拒绝的Promise正确处理异常可以让我们的代码更加简洁,排查问题也会更加方便//error?try{//可能出错的代码}catch(e){console.log(e);}//correct?try{//可能出错的代码}catch(e){//比console.log更合适console.error(e);//通知用户alertUserOfError(e);//通知服务器reportErrorToServer(e);//使用自定义异常处理thrownewCustomError(e);}Comments只对复杂逻辑添加注释。注释不要加太多,复杂逻辑加注释即可。//error?functiongenerateHash(str){//哈希变量lethash=0;//获取字符串的长度letlength=str.length;//如果长度为空则返回if(!length){returnhash;}//遍历字符for(leti=0;i
