程式碼規範整理
變數、常量
駝峰(使用有意義且可發音)命名變數、函式名、方法名
大寫命名常量(單詞間用“”)
使用解釋變數(佔位符)【】
使用預設引數代替短路或條件
函式、方法
使用物件做函式引數(理性情況引數個數為2個或更少)
forEach前先過濾,不在forEach中做判斷(函式應該只做一件事)
函式應該只有一個抽象
刪除重複程式碼
使用Object.assign設定預設物件
不要將標示(用作判斷的東西)用作函式引數
不寫入全域性函式
支援函數語言程式設計而不是指令式程式設計
封裝條件語句
避免否定條件句
避免條件句(使用多型性來實現相同的任務)
避免型別檢查(typeof、instanceof)(可藉助typescript)
不要過度優化
刪除死程式碼
物件和資料結構
使用getters和setters
使物件具有私有成員(可以通過閉包實現、private)
類(Class)
與ES5普通功能相比,更喜歡ES2015/ES6級別
使用方法連結(return this)
選擇組合而不是繼承
實體
單一責任原則(SRP)
正如Clean程式碼中所述,“一個類的更改原因不應該超過一個”。將一個類塞滿大量功能是很誘人,例如當你在你的航班上只能帶一個手提箱。這樣做的問題是,你的類在概念上沒有凝聚力,它會給它很多改變的理由。儘可能減少更改類的次數非常重要。這一點很重要,因為如果一個類中有太多的功能,而您修改了其中的一部分,那麼很難理解這將如何影響程式碼庫中的其他依賴模組。
Bad:
class UserSettings {
constructor(user) {
this.user = user;
}
changeSettings(settings) {
if (this.verifyCredentials()) {
// ...
}
}
verifyCredentials() {
// ...
}
}
Good:
class UserAuth {
constructor(user) {
this.user = user;
}
verifyCredentials() {
// ...
}
}
class UserSettings {
constructor(user) {
this.user = user;
this.auth = new UserAuth(user);
}
changeSettings(settings) {
if (this.auth.verifyCredentials()) {
// ...
}
}
}
開/關原理(OCP)
正如bertrandmeyer所說,“軟體實體(類、模組、函式等)應該是開放的,但是對於修改是關閉的。”這意味著什麼呢?這個原則基本上是說,您應該允許使用者在不改變現有程式碼的情況下新增新功能。
Bad:
class AjaxAdapter extends Adapter {
constructor() {
super();
this.name = "ajaxAdapter";
}
}
class NodeAdapter extends Adapter {
constructor() {
super();
this.name = "nodeAdapter";
}
}
class HttpRequester {
constructor(adapter) {
this.adapter = adapter;
}
fetch(url) {
if (this.adapter.name === "ajaxAdapter") {
return makeAjaxCall(url).then(response => {
// transform response and return
});
} else if (this.adapter.name === "nodeAdapter") {
return makeHttpCall(url).then(response => {
// transform response and return
});
}
}
}
function makeAjaxCall(url) {
// request and return promise
}
function makeHttpCall(url) {
// request and return promise
}
Good:
class AjaxAdapter extends Adapter {
constructor() {
super();
this.name = "ajaxAdapter";
}
request(url) {
// request and return promise
}
}
class NodeAdapter extends Adapter {
constructor() {
super();
this.name = "nodeAdapter";
}
request(url) {
// request and return promise
}
}
class HttpRequester {
constructor(adapter) {
this.adapter = adapter;
}
fetch(url) {
return this.adapter.request(url).then(response => {
// transform response and return
});
}
}
里斯科夫Liskov替代原理(LSP)
這是一個非常簡單的概念的可怕術語。它的正式定義是“如果s是T的一個子型別,那麼T型別的物件可以被s型別的物件替換(即,s型別的物件可以替換T型別的物件),而不會改變該程式的任何期望屬性(正確性、執行的任務等)”,這是一個更可怕的定義。
最好的解釋是,如果有父類和子類,那麼基類和子類可以互換使用,而不會得到錯誤的結果。這可能仍然令人困惑,所以讓我們看看經典的方形矩形示例。從數學上講,正方形是一個矩形,但如果通過繼承使用“is-a”關係對其進行建模,則很快就會遇到麻煩。
Bad:
class Rectangle {
constructor() {
this.width = 0;
this.height = 0;
}
setColor(color) {
// ...
}
render(area) {
// ...
}
setWidth(width) {
this.width = width;
}
setHeight(height) {
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
class Square extends Rectangle {
setWidth(width) {
this.width = width;
this.height = width;
}
setHeight(height) {
this.width = height;
this.height = height;
}
}
function renderLargeRectangles(rectangles) {
rectangles.forEach(rectangle => {
rectangle.setWidth(4);
rectangle.setHeight(5);
const area = rectangle.getArea(); // BAD: Returns 25 for Square. Should be 20.
rectangle.render(area);
});
}
const rectangles = [new Rectangle(), new Rectangle(), new Square()];
renderLargeRectangles(rectangles);
Good:
class Shape {
setColor(color) {
// ...
}
render(area) {
// ...
}
}
class Rectangle extends Shape {
constructor(width, height) {
super();
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
class Square extends Shape {
constructor(length) {
super();
this.length = length;
}
getArea() {
return this.length * this.length;
}
}
function renderLargeShapes(shapes) {
shapes.forEach(shape => {
const area = shape.getArea();
shape.render(area);
});
}
const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)];
renderLargeShapes(shapes);
介面隔離原理
JavaScript沒有介面,所以這個原則並不像其他原則那樣嚴格適用。然而,即使在JavaScript缺少型別系統的情況下,它也非常重要和相關。
ISP宣告“不應該強迫客戶端依賴於他們不使用的介面。”介面是JavaScript中的隱式契約,因為duck型別。
在JavaScript中演示這一原理的一個很好的例子是針對需要大型設定物件的類。不要求客戶端設定大量的選項是有益的,因為大多數時候他們不需要所有的設定。將它們設為可選有助於防止出現“胖介面”。
Bad:
class DOMTraverser {
constructor(settings) {
this.settings = settings;
this.setup();
}
setup() {
this.rootNode = this.settings.rootNode;
this.settings.animationModule.setup();
}
traverse() {
// ...
}
}
const $ = new DOMTraverser({
rootNode: document.getElementsByTagName("body"),
animationModule() {} // Most of the time, we won't need to animate when traversing.
// ...
});
Good:
class DOMTraverser {
constructor(settings) {
this.settings = settings;
this.options = settings.options;
this.setup();
}
setup() {
this.rootNode = this.settings.rootNode;
this.setupOptions();
}
setupOptions() {
if (this.options.animationModule) {
// ...
}
}
traverse() {
// ...
}
}
const $ = new DOMTraverser({
rootNode: document.getElementsByTagName("body"),
options: {
animationModule() {}
}
});
依賴倒置原理(DIP)
這一原則闡述了兩個基本事項:
高階模組不應依賴於低階模組。兩者都應該依賴於抽象。
抽象不應依賴細節。細節應該依靠抽象。
這一點一開始可能很難理解,但如果您使用過AngularJS,您就會看到依賴注入(DI)形式的這一原則的實現。雖然它們不是完全相同的概念,但是DIP阻止高階模組瞭解其低階模組的細節並設定它們。它可以通過DI實現這一點。這樣做的一個巨大好處是減少了模組之間的耦合。耦合是一種非常糟糕的開發模式,因為它使程式碼很難重構。
如前所述,JavaScript沒有介面,因此依賴的抽象是隱式契約。也就是說,一個物件/類向另一個物件/類公開的方法和屬性。在下面的示例中,隱式約定是InventoryTracker的任何請求模組都將具有一個requestItems方法。
Bad:
class InventoryRequester {
constructor() {
this.REQ_METHODS = ["HTTP"];
}
requestItem(item) {
// ...
}
}
class InventoryTracker {
constructor(items) {
this.items = items;
// BAD: We have created a dependency on a specific request implementation.
// We should just have requestItems depend on a request method: `request`
this.requester = new InventoryRequester();
}
requestItems() {
this.items.forEach(item => {
this.requester.requestItem(item);
});
}
}
const inventoryTracker = new InventoryTracker(["apples", "bananas"]);
inventoryTracker.requestItems();
Good:
class InventoryTracker {
constructor(items, requester) {
this.items = items;
this.requester = requester;
}
requestItems() {
this.items.forEach(item => {
this.requester.requestItem(item);
});
}
}
class InventoryRequesterV1 {
constructor() {
this.REQ_METHODS = ["HTTP"];
}
requestItem(item) {
// ...
}
}
class InventoryRequesterV2 {
constructor() {
this.REQ_METHODS = ["WS"];
}
requestItem(item) {
// ...
}
}
// By constructing our dependencies externally and injecting them, we can easily
// substitute our request module for a fancy new one that uses WebSockets.
const inventoryTracker = new InventoryTracker(
["apples", "bananas"],
new InventoryRequesterV2()
);
inventoryTracker.requestItems();
測試
每個測試的單一概念
併發
使用Promises,而不是callbacks(回撥)
使用Async/Await比使用Promises程式碼更乾淨
錯誤處理
不要忽略捕捉到的錯誤
不要忽視被拒絕(失敗)的Promises(請求)
格式化
使用一致的大寫字母
函式呼叫方和被呼叫方應接近(保持函式呼叫方在被呼叫方的上面)
註釋
只註釋具有業務邏輯複雜性的內容
不要在程式碼庫中留下注釋掉的程式碼
不需要日誌註釋
避免使用位置標記
相關文章
- .Net編碼規範整理
- PHP 規範 - Symfony 程式碼規範PHP
- 前端單體編碼規範整理前端
- 程式碼規範之前端編寫碼規範前端
- 程式碼分支規範
- JS程式碼規範JS
- Less程式碼規範
- css程式碼規範CSS
- iOS程式碼規範iOS
- [C#] 程式碼規範C#
- 大廠程式碼規範
- Git程式碼提交規範Git
- 程式碼規範淺談
- Android 程式碼規範大全Android
- CSS 程式碼格式規範CSS
- 基於專案實戰整理的一份 Flutter 程式碼規範與目錄規範v1.0Flutter
- Java 程式碼規範if巢狀Java巢狀
- JavaScript寫程式碼要規範JavaScript
- Airbnb JavaScript程式碼規範(完整)AIJavaScript
- HTML 程式碼註釋規範HTML
- 談談BEM規範(含程式碼)
- Python程式碼規範性檢測Python
- Vue eslint 團隊程式碼規範VueEsLint
- HTML、CSS程式碼書寫規範HTMLCSS
- 淺談專案程式碼規範
- 前端工程程式碼規範(四)——JS前端JS
- 前端工程程式碼規範(二)——HTML前端HTML
- C#規範整理·集合和LinqC#
- Css規範整理:2、css盒模型CSS模型
- 花費 3 天時間整理的程式碼規範示例程式碼,你確定不進來看看嗎?
- 上位機程式設計編碼規範程式設計
- 程式碼規範設定常見英文
- 團隊程式碼規範 ESLint + Prettier + EditorConfigEsLint
- 一些常用的程式碼規範
- 前端程式碼規範 — JavaScript 風格指南前端JavaScript
- Flutter程式碼規範優化記錄Flutter優化
- 反正舉例教你規範寫程式碼
- flutter如何統一程式碼規範Flutter