摘要:代碼整潔之道整潔的代碼不僅僅是讓人看起來舒服,更重要的是遵循一些規(guī)范能夠讓你的代碼更容易維護(hù),同時(shí)降低幾率。另外這不是強(qiáng)制的代碼規(guī)范,就像原文中說的,。里式替換原則父類和子類應(yīng)該可以被交換使用而不會(huì)出錯(cuò)。注釋好的代碼是自解釋的。
JavaScript代碼整潔之道
整潔的代碼不僅僅是讓人看起來舒服,更重要的是遵循一些規(guī)范能夠讓你的代碼更容易維護(hù),同時(shí)降低bug幾率。
原文clean-code-javascript,這里總結(jié)摘錄出個(gè)人覺得有幫助的地方,也加入了一些自己的理解(有些文字我感覺保留原文更好,所以直接copy了),其他還有很多點(diǎn)沒有列出來,感興趣可以去看原文。另外這不是強(qiáng)制的代碼規(guī)范,就像原文中說的,These are guidelines and nothing more。1. 用命名的變量代替數(shù)組下標(biāo)
// bad
const address = "One Infinite Loop, Cupertino 95014";
const cityZipCodeRegex = /^[^,]+[,s]+(.+?)s*(d{5})?$/;
saveCityZipCode(
// 下標(biāo)1,2不易于理解
address.match(cityZipCodeRegex)[1],
address.match(cityZipCodeRegex)[2]
);
// good
const address = "One Infinite Loop, Cupertino 95014";
const cityZipCodeRegex = /^[^,]+[,s]+(.+?)s*(d{5})?$/;
// 使用數(shù)組解構(gòu)更好的命名變量
const [, city, zipCode] = address.match(cityZipCodeRegex) || [];
saveCityZipCode(city, zipCode);
2. 函數(shù)的參數(shù)最好<=2個(gè),盡量避免3個(gè)。
如果有很多參數(shù)就利用object傳遞,并使用解構(gòu)。
注意object array這些引用類型的值是mutable的。3. 一個(gè)函數(shù)只做一件事。
好處在于compose, test, and reason about。
4. 不要自行擴(kuò)展原型如果想擴(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));
}
}
5. 用多態(tài)來代替條件語句
// bad
if (type === "text") {
// do something
} else if (type === "select") {
// do something else
}
我個(gè)人寫這種代碼的一種常用方式是:
const control = {
text: {
mapper() {},
restore(){},
name: "this is a text field",
},
select: {
mapper() {},
restore(){},
name: "this is a select field",
}
}
control[type].mapper();
實(shí)際上就是多態(tài)(polymorphism),也可以考慮用class的方式,大概這樣:
class Field {
...
}
class TextField extends Field {
mapper(){}
restore(){}
name = "this is a text field";
}
class SelectField extends Field {
mapper(){}
restore(){}
name = "this is a select field";
}
6. 使用getter和setter函數(shù)。
// 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里面做很多事情而不需要修改每一個(gè).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 單一職責(zé)原則There should never be more than one reason for a class to change,一個(gè)類被改變的原因數(shù)量應(yīng)該盡可能降低。如果一個(gè)類中功能太多,當(dāng)你修改其中一點(diǎn)時(shí)會(huì)無法估量任何引用該類的模塊所受到的影響。
Open/Closed Principle 開放封閉原則用戶可以在不修改內(nèi)部實(shí)現(xiàn)的前提下自行擴(kuò)展功能。例如有一個(gè)Http模塊,內(nèi)部會(huì)根據(jù)環(huán)境判斷用哪個(gè)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 里式替換原則
父類和子類應(yīng)該可以被交換使用而不會(huì)出錯(cuò)。
// 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,因?yàn)闀?huì)導(dǎo)致計(jì)算面積錯(cuò)誤,考慮將計(jì)算面積的方法抽象出來:
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。舉例來說,一個(gè)功能模塊需要設(shè)計(jì)必須傳的參數(shù)和可選參數(shù),不應(yīng)該強(qiáng)迫用戶使用可選參數(shù)。
Dependency Inversion Principle 依賴注入原則原文:
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend upon details. Details should depend on abstractions.
// 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內(nèi)部實(shí)例化了InventoryRequester,也就意味著high-level的模塊需要知道low-level模塊的細(xì)節(jié)(比如實(shí)例化InventoryRequester需要知道它的構(gòu)造參數(shù)等,或者說需要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的實(shí)例而不需要考慮它是如何被實(shí)例化的,high-level只需要依賴抽象的接口就可以完成對(duì)子模塊的調(diào)用。
9. 注釋Comments are an apology, not a requirement. Good code mostly documents itself. 好的代碼是自解釋的。
參考:clean-code-javascript
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/103083.html
摘要:概述在代碼整潔之道中提到的軟件工程原則,同樣適用于。我們?cè)谲浖こ谭矫娴募夹g(shù)發(fā)展剛剛超過年,我們?nèi)匀辉趯W(xué)習(xí)很多東西。不好好一個(gè)函數(shù)只做一件事目前這是軟件工程中最重要的原則。 概述 showImg(https://segmentfault.com/img/remote/1460000008066597?w=500&h=471); Robert C. Martin 在《代碼整潔之道》 中提...
摘要:在代碼整潔之道,提出一種軟件質(zhì)量,可持續(xù)開發(fā)不僅在于項(xiàng)目架構(gòu)設(shè)計(jì),還與代碼質(zhì)量密切相關(guān),代碼的整潔度和質(zhì)量成正比,一份整潔的代碼在質(zhì)量上是可靠的,為團(tuán)隊(duì)開發(fā),后期維護(hù),重構(gòu)奠定了良好的基礎(chǔ)。 現(xiàn)在的軟件系統(tǒng)開發(fā)難度主要在于其復(fù)雜度和規(guī)模,客戶需求也不再像Winston Royce瀑布模型期望那樣在系統(tǒng)編碼前完成所有的設(shè)計(jì)滿足用戶軟件需求。在這個(gè)信息爆炸技術(shù)日新月異的時(shí)代,需求總是在不停...
摘要:我們這里再介紹一下,朱重八家族的名字,都很有特點(diǎn)。取這樣的名字不是因?yàn)橹旒沂歉銛?shù)學(xué)的,而是因?yàn)樵谠习傩杖绻荒苌蠈W(xué)和當(dāng)官就沒有名字,只能以父母年齡相加或者出生的日期命名。所以說命名不僅僅是一種科學(xué),更是一種藝術(shù)。 在小朱元璋出生一個(gè)月后,父母為他取了一個(gè)名字(元時(shí)慣例):朱重八,這個(gè)名字也可以叫做朱八八。我們這里再介紹一下,朱重八家族的名字,都很有特點(diǎn)。朱重八高祖名字:朱百六;朱...
摘要:我們這里再介紹一下,朱重八家族的名字,都很有特點(diǎn)。取這樣的名字不是因?yàn)橹旒沂歉銛?shù)學(xué)的,而是因?yàn)樵谠?,老百姓如果不能上學(xué)和當(dāng)官就沒有名字,只能以父母年齡相加或者出生的日期命名。所以說命名不僅僅是一種科學(xué),更是一種藝術(shù)。 在小朱元璋出生一個(gè)月后,父母為他取了一個(gè)名字(元時(shí)慣例):朱重八,這個(gè)名字也可以叫做朱八八。我們這里再介紹一下,朱重八家族的名字,都很有特點(diǎn)。朱重八高祖名字:朱百六;朱...
閱讀 1586·2023-04-25 15:40
閱讀 3048·2021-08-11 11:15
閱讀 2339·2019-08-26 13:48
閱讀 2903·2019-08-26 12:18
閱讀 2518·2019-08-23 18:23
閱讀 2966·2019-08-23 17:01
閱讀 3046·2019-08-23 16:29
閱讀 1183·2019-08-23 15:15