JavaScript 程式碼整潔之道
整潔的程式碼不僅僅是讓人看起來舒服,更重要的是遵循一些規範能夠讓你的程式碼更容易維護,同時降低bug機率。
1. 用命名的變數代替陣列下標
// bad const address = "One Infinite Loop, Cupertino 95014"; const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; saveCityZipCode( // 下標1,2不易於理解 address.match(cityZipCodeRegex)[1], address.match(cityZipCodeRegex)[2] );
// good const address = "One Infinite Loop, Cupertino 95014"; const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; // 使用陣列解構更好的命名變數 const [, city, zipCode] = address.match(cityZipCodeRegex) || []; saveCityZipCode(city, zipCode);
2. 函式的引數最好<=2個,儘量避免3個。
如果有很多引數就利用object傳遞,並使用解構。
3. 一個函式只做一件事。
好處在於compose, test, and reason about。
4. 不要自行擴充套件原型
如果想擴充套件原型,可以先繼承再新增方法,防止汙染。
// 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)); } }
5. 用多型來代替條件語句
// bad if (type === 'text') { // do something } else if (type === 'select') { // do something else }
個人寫這種程式碼的一種常用方式是:
const control = { text: { mapper() {}, restore(){}, name: 'this is a text field', }, select: { mapper() {}, restore(){}, name: 'this is a select field', } } control[type].mapper();
實際上就是多型(polymorphism),也可以考慮用class的方式,大概這樣:
class Field { ... } class TextField extends Field { mapper(){} restore(){} name = 'this is a text field'; } class SelectField extends Field { mapper(){} restore(){} name = 'this i```s a select field'; }
- 使用getter和setter函式。
// bad function makeBankAccount() { // ... return { balance: 0 // ... }; } const account = makeBankAccount(); account.balance = 100;
// good function makeBankAccount() { // this one is private let balance = 0; // a "getter", made public via the returned object below function getBalance() { return balance; } // a "setter", made public via the returned object below function setBalance(amount) { // ... validate before updating the balance balance = amount; } return { // ... getBalance, setBalance }; } const account = makeBankAccount(); account.setBalance(100);
你可以在getter和setter裡面做很多事情而不需要修改每一個.balance的地方。
7. Prefer composition over inheritance
儘量用組合來代替繼承,什麼情況下用繼承:
Your inheritance represents an "is-a" relationship and not a "has-a" relationship (Human->Animal vs. User->UserDetails).
You can reuse code from the base classes (Humans can move like all animals).
You want to make global changes to derived classes by changing a base class. (Change the caloric expenditure of all animals when they move).
8. SOLID
Single Responsibility Principle 單一職責原則
There should never be more than one reason for a class to change,一個類被改變的原因數量應該儘可能降低。如果一個類中---功能太多,當你修改其中一點時會無法估量任何引用該類的模組所受到的影響。
Open/Closed Principle 開放封閉原則
使用者可以在不修改內部實現的前提下自行擴充套件功能。例如有一個Http模組,內部會根據環境判斷用哪個adaptor。如果使用者要新增adaptor就必須修改Http模組。
// 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 Substitution Principle 裡式替換原則
父類和子類應該可以被交換使用而不會出錯。
// 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);
上面的Rectangle不能直接替換Square,因為會導致計算面積錯誤,考慮將計算面積的方法抽象出來:
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);
Interface Segregation Principle 介面隔離原則
Clients should not be forced to depend upon interfaces that they do not use。舉例來說,一個功能模組需要設計必須傳的引數和可選引數,不應該強迫使用者使用可選引數。
Dependency Inversion Principle 依賴注入原則
// 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();
上面例子在於,InventoryTracker內部例項化了InventoryRequester,也就意味著high-level的模組需要知道low-level模組的細節(比如例項化InventoryRequester需要知道它的構造引數等,或者說需要import該模組,造成耦合)。
// 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();
直接傳入low-level的例項而不需要考慮它是如何被例項化的,high-level只需要依賴抽象的介面就可以完成對子模組的呼叫。
9. 註釋
Comments are an apology, not a requirement. Good code mostly documents itself. 好的程式碼是自解釋的。
你會經常地遇到 bug 和其它一些問題。這可能會讓人沮喪,但你要儘量保持冷靜,並系統地去思考。記住實踐是解決問題的最佳方法。
我們採集的是石頭,但是必須時刻展望未來的大教堂。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69901074/viewspace-2639805/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 程式碼整潔之道
- Typescript 程式碼整潔之道TypeScript
- 聊聊程式碼整潔之道
- 程式碼整潔之道 clean code
- 重構 - 程式碼整潔之道
- JavaScript 程式碼簡潔之道JavaScript
- JavaScript程式碼簡潔之道JavaScript
- 程式碼整潔之道讀書記
- 閱讀《程式碼整潔之道》總結
- 程式碼整潔之道 – 有意義的命名
- 程式碼整潔之道之做減法
- 程式碼整潔之道的 7 個方法
- 程式碼整潔之道Clean Code筆記筆記
- 《程式碼整潔之道》總結和筆記筆記
- 讀書筆記-程式碼整潔之道(一)筆記
- 程式碼整潔之道--讀書筆記(14)筆記
- 程式碼整潔之道--讀書筆記(1)筆記
- 程式碼整潔之道--讀書筆記(2)筆記
- 程式碼整潔之道--讀書筆記(3)筆記
- 程式碼整潔之道--讀書筆記(4)筆記
- 程式碼整潔之道--讀書筆記(5)筆記
- 程式碼整潔之道--讀書筆記(6)筆記
- 程式碼整潔之道--讀書筆記(7)筆記
- 程式碼整潔之道--讀書筆記(9)筆記
- 程式碼整潔之道--讀書筆記(10)筆記
- 程式碼整潔之道--讀書筆記(11)筆記
- 程式碼整潔之道--讀書筆記(12)筆記
- 程式碼整潔之道--讀書筆記(13)筆記
- PHP 整潔之道PHP
- 《程式碼整潔之道》精華速覽,助你提升程式碼質量
- Python程式碼整潔之道--使用裝飾器改進程式碼Python
- 程式碼整潔之道:程式設計師的職業素養(十三)程式設計師
- 程式碼整潔之道 讀書筆記(一) 第11章 系統筆記
- 程式碼整潔之道 讀書筆記(二)第12章 迭進筆記
- 寫出整潔的 JavaScript 程式碼 ES6 版JavaScript
- 架構整潔之道-書中箴言架構箴言
- JS程式碼簡潔之道--函式JS函式
- Clean Code PHP 程式碼簡潔之道PHP