這樣寫的 JS 代碼看著就很舒服
來自公眾號:小生方勤
作者:小生方勤
如何提高代碼的可讀性、復(fù)用性、擴(kuò)展性。我們將從以下四個方面討論:
變量 函數(shù) 類 異步
一、變量
用有意義且常用的單詞命名
//?Bad:
const?yyyymmdstr?=?moment().format('YYYY/MM/DD');
//?Good:
const?currentDate?=?moment().format('YYYY/MM/DD');
保持統(tǒng)一
對同一類型的變量使用相同的命名保持統(tǒng)一:
//?Bad:
getUserInfo();
getClientData();
getCustomerRecord();
//?Good:
getUser()
每個常量(全大寫)都該命名
可以用?ESLint?檢測代碼中未命名的常量。
//?Bad:
//?其他人知道 86400000?的意思嗎?
setTimeout(?blastOff,?86400000?);
//?Good:
const?MILLISECOND_IN_A_DAY?=?86400000;
setTimeout(?blastOff,?MILLISECOND_IN_A_DAY?);
避免無意義的前綴
如果創(chuàng)建了一個對象 car,就沒有必要把它的顏色命名為 carColor。
//?Bad:
const?car?=?{
????carMake:?'Honda',
????carModel:?'Accord',
????carColor:?'Blue'
};
function?paintCar(?car?)?{
????car.carColor?=?'Red';
}
//?Good:
const?car?=?{
????make:?'Honda',
????model:?'Accord',
????color:?'Blue'
};
function?paintCar(?car?)?{
????car.color?=?'Red';
}
傳參使用默認(rèn)值
//?Bad:
function?createMicrobrewery(?name?)?{
????const?breweryName?=?name?||?'Hipster?Brew?Co.';
????//?...
}
//?Good:
function?createMicrobrewery(?name?=?'Hipster?Brew?Co.'?)?{
????//?...
}
二、函數(shù)
函數(shù)參數(shù)( 最好 2 個或更少 )
如果參數(shù)超過兩個,建議使用 ES6 的解構(gòu)語法,不用考慮參數(shù)的順序。
//?Bad:
function?createMenu(?title,?body,?buttonText,?cancellable?)?{
????//?...
}
//?Good:
function?createMenu(?{?title,?body,?buttonText,?cancellable?}?)?{
????//?...
}
createMenu({
????title:?'Foo',
????body:?'Bar',
????buttonText:?'Baz',
????cancellable:?true
});
一個方法只做一件事情
這是一條在軟件工程領(lǐng)域流傳久遠(yuǎn)的規(guī)則。嚴(yán)格遵守這條規(guī)則會讓你的代碼可讀性更好,也更容易重構(gòu)。如果違反這個規(guī)則,那么代碼會很難被測試或者重用。
//?Bad:
function?emailClients(?clients?)?{
????clients.forEach(?client?=>?{
????????const?clientRecord?=?database.lookup(?client?);
????????if?(?clientRecord.isActive()?)?{
????????????email(?client?);
????????}
????});
}
//?Good:
function?emailActiveClients(?clients?)?{
????clients
????????.filter(?isActiveClient?)
????????.forEach(?email?);
}
function?isActiveClient(?client?)?{
????const?clientRecord?=?database.lookup(?client?);????
????return?clientRecord.isActive();
}
函數(shù)名上體現(xiàn)它的作用
//?Bad:
function?addToDate(?date,?month?)?{
????//?...
}
const?date?=?new?Date();
//?很難知道是把什么加到日期中
addToDate(?date,?1?);
//?Good:
function?addMonthToDate(?month,?date?)?{
????//?...
}
const?date?=?new?Date();
addMonthToDate(?1,?date?);
刪除重復(fù)代碼,合并相似函數(shù)
很多時候雖然是同一個功能,但由于一兩個不同點,讓你不得不寫兩個幾乎相同的函數(shù)。
//?Bad:
function?showDeveloperList(developers)?{
??developers.forEach((developer)?=>?{
????const?expectedSalary?=?developer.calculateExpectedSalary();
????const?experience?=?developer.getExperience();
????const?githubLink?=?developer.getGithubLink();
????const?data?=?{
??????expectedSalary,
??????experience,
??????githubLink
????};
????render(data);
??});
}
function?showManagerList(managers)?{
??managers.forEach((manager)?=>?{
????const?expectedSalary?=?manager.calculateExpectedSalary();
????const?experience?=?manager.getExperience();
????const?portfolio?=?manager.getMBAProjects();
????const?data?=?{
??????expectedSalary,
??????experience,
??????portfolio
????};
????render(data);
??});
}
//?Good:
function?showEmployeeList(employees)?{
??employees.forEach(employee?=>?{
????const?expectedSalary?=?employee.calculateExpectedSalary();
????const?experience?=?employee.getExperience();
????const?data?=?{
??????expectedSalary,
??????experience,
????};
????switch(employee.type)?{
??????case?'develop':
????????data.githubLink?=?employee.getGithubLink();
????????break
??????case?'manager':
????????data.portfolio?=?employee.getMBAProjects();
????????break
????}
????render(data);
??})
}
使用 Object.assign 設(shè)置默認(rèn)屬性
//?Bad:
const?menuConfig?=?{
??title:?null,
??body:?'Bar',
??buttonText:?null,
??cancellable:?true
};
function?createMenu(config)?{
??config.title?=?config.title?||?'Foo';
??config.body?=?config.body?||?'Bar';
??config.buttonText?=?config.buttonText?||?'Baz';
??config.cancellable?=?config.cancellable?!==?undefined???config.cancellable?:?true;
}
createMenu(menuConfig);
//?Good:
const?menuConfig?=?{
??title:?'Order',
??//?不包含?body
??buttonText:?'Send',
??cancellable:?true
};
function?createMenu(config)?{
??config?=?Object.assign({
????title:?'Foo',
????body:?'Bar',
????buttonText:?'Baz',
????cancellable:?true
??},?config);
??//?config?:?{title:?"Order",?body:?"Bar",?buttonText:?"Send",?cancellable:?true}
??//?...
}
createMenu(menuConfig);
盡量不要寫全局方法
在 JavaScript 中,永遠(yuǎn)不要污染全局,會在生產(chǎn)環(huán)境中產(chǎn)生難以預(yù)料的 bug。舉個例子,比如你在 Array.prototype 上新增一個 diff 方法來判斷兩個數(shù)組的不同。而你同事也打算做類似的事情,不過他的 diff 方法是用來判斷兩個數(shù)組首位元素的不同。很明顯你們方法會產(chǎn)生沖突,遇到這類問題我們可以用 ES2015/ES6 的語法來對 Array 進(jìn)行擴(kuò)展。
//?Bad:
Array.prototype.diff?=?function?diff(comparisonArray)?{
??const?hash?=?new?Set(comparisonArray);
??return?this.filter(elem?=>?!hash.has(elem));
};
//?Good:
class?SuperArray?extends?Array?{
??diff(comparisonArray)?{
????const?hash?=?new?Set(comparisonArray);
????return?this.filter(elem?=>?!hash.has(elem));????????
??}
}
盡量別用“非”條件句
//?Bad:
function?isDOMNodeNotPresent(node)?{
??//?...
}
if?(!isDOMNodeNotPresent(node))?{
??//?...
}
//?Good:
function?isDOMNodePresent(node)?{
??//?...
}
if?(isDOMNodePresent(node))?{
??//?...
}
不要過度優(yōu)化
現(xiàn)代瀏覽器已經(jīng)在底層做了很多優(yōu)化,過去的很多優(yōu)化方案都是無效的,會浪費你的時間。
//?Bad:
//?現(xiàn)代瀏覽器已對此(?緩存 list.length )做了優(yōu)化。
for?(let?i?=?0,?len?=?list.length;?i???//?...
}
//?Good:
for?(let?i?=?0;?i???//?...
}
刪除棄用代碼
這里沒有實例代碼,刪除就對了
三、類
使用 ES6 的 class
在 ES6 之前,沒有類的語法,只能用構(gòu)造函數(shù)的方式模擬類,可讀性非常差。
//?Good:
//?動物
class?Animal?{
??constructor(age)?{
????this.age?=?age
??};
??move()?{};
}
//?哺乳動物
class?Mammal?extends?Animal{
??constructor(age,?furColor)?{
????super(age);
????this.furColor?=?furColor;
??};
??liveBirth()?{};
}
//?人類
class?Human?extends?Mammal{
??constructor(age,?furColor,?languageSpoken)?{
????super(age,?furColor);
????this.languageSpoken?=?languageSpoken;
??};
??speak()?{};
}
使用鏈?zhǔn)秸{(diào)用
這種模式相當(dāng)有用,可以在很多庫中都有使用。它讓你的代碼簡潔優(yōu)雅。
class?Car?{
??constructor(make,?model,?color)?{
????this.make?=?make;
????this.model?=?model;
????this.color?=?color;
??}
??setMake(make)?{
????this.make?=?make;
??}
??setModel(model)?{
????this.model?=?model;
??}
??setColor(color)?{
????this.color?=?color;
??}
??save()?{
????console.log(this.make,?this.model,?this.color);
??}
}
//?Bad:
const?car?=?new?Car('Ford','F-150','red');
car.setColor('pink');
car.save();
//?Good:
const?car?=?new?Car('Ford','F-150','red')
??.setColor('pink');
??.save();
四、異步
使用 promise 或者 Async/Await 代替回調(diào)
//?Bad:
get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin',?(requestErr,?response)?=>?{
??if?(requestErr)?{
????console.error(requestErr);
??}?else?{
????writeFile('article.html',?response.body,?(writeErr)?=>?{
??????if?(writeErr)?{
????????console.error(writeErr);
??????}?else?{
????????console.log('File?written');
??????}
????});
??}
});
//?Good:
get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin')
??.then((response)?=>?{
????return?writeFile('article.html',?response);
??})
??.then(()?=>?{
????console.log('File?written');
??})
??.catch((err)?=>?{
????console.error(err);
??});
//?perfect:
async?function?getCleanCodeArticle()?{
??try?{
????const?response?=?await?get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin');
????await?writeFile('article.html',?response);
????console.log('File?written');
??}?catch(err)?{
????console.error(err);
??}
}
最后
關(guān)注公眾號【前端宇宙】,每日獲取好文推薦 添加微信,入群交流 “在看和轉(zhuǎn)發(fā)”就是最大的支持
