如何写出干净的 JavaScript 代码

更新日期: 2021-11-29阅读: 989标签: 代码

一段干净的代码,你在阅读、重用和重构的时候都能非常轻松。编写干净的代码非常重要,因为在我们日常的工作中,你不是仅仅是在为自己写代码。实际上,你还需要考虑一群需要理解、编辑和构建你的代码的同事。

1. 变量

使用有意义的名称

变量的名称应该是可描述,有意义的,JavaScript变量都应该采用驼峰式大小写 (camelCase) 命名。

//Don't❌
constfoo="JDoe@example.com";
constbar="John";
constage=23;
constqux=true;

//Do✅
constemail="John@example.com";
constfirstName="John";
constage=23;
constisActive=true

布尔变量通常需要回答特定问题,例如:

isActive
didSubscribe
hasLinkedAccount

避免添加不必要的上下文

当对象或类已经包含了上下文的命名时,不要再向变量名称添加冗余的上下文。

//Don't❌
constuser={
userId:"296e2589-7b33-400a-b762-007b730c8e6d",
userEmail:"JDoe@example.com",
userFirstName:"John",
userLastName:"Doe",
userAge:23,
};

user.userId;

//Do✅
constuser={
id:"296e2589-7b33-400a-b762-007b730c8e6d",
email:"JDoe@example.com",
firstName:"John",
lastName:"Doe",
age:23,
};

user.id;

避免硬编码值

确保声明有意义且可搜索的常量,而不是直接插入一个常量值。全局常量可以采用SCREAMING_SNAKE_CASE风格命名。

//Don't❌
setTimeout(clearSessionData,900000);

//Do✅
constSESSION_DURATION_MS=15*60*1000;

setTimeout(clearSessionData,SESSION_DURATION_MS);

2. 函数

使用有意义的名称

函数名称需要描述函数的实际作用,即使很长也没关系。函数名称通常使用动词,但返回布尔值的函数可能是个例外 — 它可以采用是或否问题的形式,函数名也应该是驼峰式的。

//Don't❌
functiontoggle(){
//...
}

functionagreed(user){
//...
}

//Do✅
functiontoggleThemeSwitcher(){
//...
}

functiondidAgreeToAllTerms(user){
//...
}

使用默认参数

默认参数比&& ||或在函数体内使用额外的条件语句更干净。

//Don't❌
functionprintAllFilesInDirectory(dir){
constdirectory=dir||"./";
//...
}

//Do✅
functionprintAllFilesInDirectory(dir="./"){
//...
}

限制参数的数量

尽管这条规则可能有争议,但函数最好是有3个以下参数。如果参数较多可能是以下两种情况之一:

  • 该函数做的事情太多,应该拆分。
  • 传递给函数的数据以某种方式相关,可以作为专用数据结构传递。
//Don't❌
functionsendPushNotification(title,message,image,isSilent,delayMs){
//...
}

sendPushNotification("NewMessage","...","http://...",false,1000);

//Do✅
functionsendPushNotification({title,message,image,isSilent,delayMs}){
//...
}

constnotificationConfig={
title:"NewMessage",
message:"...",
image:"http://...",
isSilent:false,
delayMs:1000,
};

sendPushNotification(notificationConfig);

避免在一个函数中做太多事情

一个函数应该一次做一件事,这有助于减少函数的大小和复杂性,使测试、调试和重构更容易。

/Don't❌
functionpingUsers(users){
users.forEach((user)=>{
constuserRecord=database.lookup(user);
if(!userRecord.isActive()){
ping(user);
}
});
}

//Do✅
functionpingInactiveUsers(users){
users.filter(!isUserActive).forEach(ping);
}

functionisUserActive(user){
constuserRecord=database.lookup(user);
returnuserRecord.isActive();
}

避免使用布尔标志作为参数

函数含有布尔标志的参数意味这个函数是可以被简化的。

//Don't❌
functioncreateFile(name,isPublic){
if(isPublic){
fs.create(`./public/${name}`);
}else{
fs.create(name);
}
}

//Do✅
functioncreateFile(name){
fs.create(name);
}

functioncreatePublicFile(name){
createFile(`./public/${name}`);
}

避免写重复的代码

如果你写了重复的代码,每次有逻辑改变,你都需要改动多个位置。

//Don't❌
functionrenderCarsList(cars){
cars.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,nbOfDoors});
});
}

//Do✅
functionrenderVehiclesList(vehicles){
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;
case"motorcycle":
data.seatHeight=vehicle.getSeatHeight();
break;
}

render(data);
});
}

避免副作用

在JavaScript中,你应该更喜欢函数式模式而不是命令式模式。换句话说,大多数情况下我们都应该保持函数纯。副作用可能会修改共享状态和资源,从而导致一些奇怪的问题。所有的副作用都应该集中管理,例如你需要更改全局变量或修改文件,可以专门写一个 util 来做这件事。

//Don't❌
letdate="21-8-2021";

functionsplitIntoDayMonthYear(){
date=date.split("-");
}

splitIntoDayMonthYear();

//Anotherfunctioncouldbeexpectingdateasastring
console.log(date);//['21','8','2021'];

//Do✅
functionsplitIntoDayMonthYear(date){
returndate.split("-");
}

constdate="21-8-2021";
constnewDate=splitIntoDayMonthYear(date);

//Originalvlaueisintact
console.log(date);//'21-8-2021';
console.log(newDate);//['21','8','2021'];

另外,如果你将一个可变值传递给函数,你应该直接克隆一个新值返回,而不是直接改变该它。

//Don't❌
functionenrollStudentInCourse(course,student){
course.push({student,enrollmentDate:Date.now()});
}

//Do✅
functionenrollStudentInCourse(course,student){
return[...course,{student,enrollmentDate:Date.now()}];
}

3. 条件语句

使用非负条件

//Don't❌
functionisUserNotVerified(user){
//...
}

if(!isUserNotVerified(user)){
//...
}

//Do✅
functionisUserVerified(user){
//...
}

if(isUserVerified(user)){
//...
}

尽可能使用简写

//Don't❌
if(isActive===true){
//...
}

if(firstName!==""&&firstName!==null&&firstName!==undefined){
//...
}

constisUserEligible=user.isVerified()&&user.didSubscribe()?true:false;

//Do✅
if(isActive){
//...
}

if(!!firstName){
//...
}

constisUserEligible=user.isVerified()&&user.didSubscribe();

避免过多分支

尽早return会使你的代码线性化、更具可读性且不那么复杂。

//Don't❌
functionaddUserService(db,user){
if(!db){
if(!db.isConnected()){
if(!user){
returndb.insert("users",user);
}else{
thrownewError("Nouser");
}
}else{
thrownewError("Nodatabaseconnection");
}
}else{
thrownewError("Nodatabase");
}
}

//Do✅
functionaddUserService(db,user){
if(!db)thrownewError("Nodatabase");
if(!db.isConnected())thrownewError("Nodatabaseconnection");
if(!user)thrownewError("Nouser");

returndb.insert("users",user);
}

优先使用 map 而不是 switch 语句

既能减少复杂度又能提升性能。

//Don't❌
constgetColorByStatus=(status)=>{
switch(status){
case"success":
return"green";
case"failure":
return"red";
case"warning":
return"yellow";
case"loading":
default:
return"blue";
}
};

//Do✅
conststatusColors={
success:"green",
failure:"red",
warning:"yellow",
loading:"blue",
};

constgetColorByStatus=(status)=>statusColors[status]||"blue";

使用可选链接

constuser={
email:"JDoe@example.com",
billing:{
iban:"...",
swift:"...",
address:{
street:"SomeStreetName",
state:"CA",
},
},
};

//Don't❌
constemail=(user&&user.email)||"N/A";
conststreet=
(user&&
user.billing&&
user.billing.address&&
user.billing.address.street)||
"N/A";
conststate=
(user&&
user.billing&&
user.billing.address&&
user.billing.address.state)||
"N/A";

//Do✅
constemail=user?.email??"N/A";
conststreet=user?.billing?.address?.street??"N/A";
conststreet=user?.billing?.address?.state??"N/A";

4.并发

避免回调

回调很混乱,会导致代码嵌套过深,使用Promise替代回调。

//Don't❌
getUser(function(err,user){
getProfile(user,function(err,profile){
getAccount(profile,function(err,account){
getReports(account,function(err,reports){
sendStatistics(reports,function(err){
console.error(err);
});
});
});
});
});

//Do✅
getUser()
.then(getProfile)
.then(getAccount)
.then(getReports)
.then(sendStatistics)
.catch((err)=>console.error(err));

//orusingAsync/Await✅✅

asyncfunctionsendUserStatistics(){
try{
constuser=awaitgetUser();
constprofile=awaitgetProfile(user);
constaccount=awaitgetAccount(profile);
constreports=awaitgetReports(account);
returnsendStatistics(reports);
}catch(e){
console.error(err);
}
}

5. 错误处理

处理抛出的错误和 reject 的 promise

/Don't❌
try{
//Possibleerronouscode
}catch(e){
console.log(e);
}

//Do✅
try{
//Possibleerronouscode
}catch(e){
//Followthemostapplicable(orall):
//1-Moresuitablethanconsole.log
console.error(e);

//2-Notifyuserifapplicable
alertUserOfError(e);

//3-Reporttoserver
reportErrorToServer(e);

//4-Useacustomerrorhandler
thrownewCustomError(e);
}

6.注释

只注释业务逻辑

可读的代码使你免于过度注释,因此,你应该只注释复杂的逻辑。

//Don't❌
functiongenerateHash(str){
//Hashvariable
lethash=0;

//Getthelengthofthestring
letlength=str.length;

//Ifthestringisemptyreturn
if(!length){
returnhash;
}

//Loopthrougheverycharacterinthestring
for(leti=0;i<length;i++){
//Getcharactercode.
constchar=str.charCodeAt(i);

//Makethehash
hash=(hash<<5)-hash+char;

//Convertto32-bitinteger
hash&=hash;
}
}

//Do✅
functiongenerateHash(str){
lethash=0;
letlength=str.length;
if(!length){
returnhash;
}

for(leti=0;i<length;i++){
constchar=str.charCodeAt(i);
hash=(hash<<5)-hash+char;
hash=hash&hash;//Convertto32bitinteger
}
returnhash;
}

使用版本控制

在代码里不需要保留历史版本的注释,想查的话你直接用git log就可以搜到。。

//Don't❌
/**
*2021-7-21:Fixedcornercase
*2021-7-15:Improvedperformance
*2021-7-10:Handledmutlipleusertypes
*/
functiongenerateCanonicalLink(user){
//constsession=getUserSession(user)
constsession=user.getSession();
//...
}

//Do✅
functiongenerateCanonicalLink(user){
constsession=user.getSession();
//...
}

好了,去写出你漂亮的代码吧!

链接: https://fly63.com/article/detial/10877

不要浪费时间写完美代码

一个系统可以维持5年,10年,甚至20年以上,但是代码和设计模式的生命周期非常短,当对一个解决方案使用不同的方法进行迭代的时候,通常只能维持数月,数日,甚至几分钟的时间

Google内部在代码质量上的实践

良好的编程习惯涉及到很多方面,但在软件行业内,大多数的公司或组织都不会把良好的编程习惯列为主要关注点。 例如,具有可读性和可维护性的代码比编写好的测试代码或使用正确的工具更有意义,前者的意义在于可以让代码更易于理解和修改。

减少嵌套,降低代码复杂度

减少嵌套会让代码可读性更好,同时也能更容易的找出bug,开发人员可以更快的迭代,程序也会越来越稳定。简化代码,让编程更轻松!

关于 Google 发布的 JS 代码规范

Google为了那些还不熟悉代码规范的人发布了一个JS代码规范。其中列出了编写简洁易懂的代码所应该做的最佳实践。代码规范并不是一种编写正确JavaScript代码的规则,而是为了保持源代码编写模式一致的一种选择。

你解决的问题比你编写的代码更重要!

程序员似乎忘记了软件的真正目的,那就是解决现实问题。您编写的代码的目的是为了创造价值并使现有世界变得更美好,而不是满足您对自我世界应该是什么的以自我为中心的观点。有人说:如果你拥有的只是一把锤子,那么一切看起来都像钉子一样

tinymce与prism代码高亮实现及汉化的配置

TinyMCE是一个轻量级的基于浏览器的所见即所得编辑器,由JavaScript写成。它对IE6+和Firefox1.5+都有着非常良好的支持。功能方强大,并且功能配置灵活简单。另一特点是加载速度非常快的。

js函数式编程与代码执行效率

函数式编程对应的是命令式编程, 函数式编程的核心当然是对函数的运用. 而高阶函数(Higher-order)是实现函数式编程的基本要素。高阶函数可以将其他函数作为参数或者返回结果。所以JS天生就支持函数式编程

接手代码太烂,要不要辞职?

朋友发表了一条说说:入职新公司,从重构代码到放弃”,我就问他怎么了?他说,刚进一家新公司,接手代码太烂,领导让我先熟悉业务逻辑,然后去修复之前项目中遗留的bug,实在不行就重构

js高亮显示关键词_页面、搜索关键词高亮显示

页面实现关键词高亮显示:在项目期间遇到一个需求,就是搜索关键词时需要高亮显示,主要通过正则匹配来实现页面关键词高亮显示。在搜索结果中高亮显示关键词:有一组关键词数组,在数组中筛选出符合关键字的内容并将关键字高亮

写优雅的代码,做优雅的程序员

软件工程学什么? 学计算机,写程序,做软件,当程序员。听说学计算机很辛苦? 是的,IT行业加班现象严重。在计算机世界里,技术日新月异,自学能力是程序员最重要的能力之一。选了这个专业,就要时刻保持好奇心和技术嗅觉,不能只满足于完成课内作业。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!