釋是軟件開發過程中的性價比極高的一個工具,它只需要花費20%的時間,即可獲取80%的價值。代碼注釋最重要的作用就是讓讀者可以在不讀源碼的情況下,快速了解一段代碼的主要功能。
相信大家都會遇到這種情況:一周前自己寫的代碼,現在再拿出來看,發現讀不懂了,“ 這代碼是我寫的???”。這時候,代碼注釋就可以發揮它的作用了——提高晦澀難懂的代碼的可讀性;注釋可以起到隱藏代碼復雜細節的作用,比如接口注釋可以幫助開發者在沒有閱讀代碼的情況下快速了解該接口的功能和用法;如果寫的好,注釋還可以改善系統的設計。
既然注釋這么多好處,為什么我們程序員還是不愿意寫注釋?
“代碼都寫不完了,哪有時間寫注釋,以后再補吧“
時間不夠論。這是最常見的原因,在交付速度飛快的今天,“代碼寫不完”是一個再常見不過的情況了,但寫注釋真的會導致需求延遲嗎?絕不!相對于寫一個接口的實現,寫接口注釋的時間可能只需要花費前者的5%。但不寫注釋,后面使用接口的人必須要多花費**50%**的時間去讀懂代碼!而且對于大部分程序員而言,“以后再補“大概要到2910年才能落實。
“好的代碼就是最好的注釋,我的代碼可讀性很好,沒必要寫注釋”
好代碼勝過注釋論。不少程序員認為,好的代碼就是最好的注釋,只要代碼可讀性好,注釋就可以省去。然而一個軟件系統很多信息是無法通過代碼呈現出來的,比如系統的設計思路、函數執行的預置條件等等。此外,代碼可讀性也不是絕對的,對于一個沒有使用過Java 8 的 Lambda表達式的開發者而言,通篇的箭頭“->“簡直就是一場噩夢。
”過期的注釋容易誤導人“
過期注釋論。不可否認,過期的注釋很容易誤導讀者,但是這并不能成為否認注釋的借口。除非是重大的重構或重寫,對注釋進行大改動的情況很少出現。通常,在更改代碼之后,只需花費極少的時間去更新注釋,就可以避免過期注釋這種情況了。
注釋大致可以分成四類:接口注釋、數據成員注釋、實現注釋和模塊依賴注釋。
平時我們所說的接口,通常指的是一個類(包括interface、class、enum)和方法。對類而言,接口注釋主要描述該類提供的功能;對方法而言,除了描述方法功能之外,方法的入參和返回值都要進行說明。當然,使用類/方法的一些預置條件和副作用等信息都需要在接口注釋中提到。
/**
* Returns the length of this string.
* The length is equal to the number of <a href="Character.html#unicode">Unicode
* code units</a> in the string.
*
* @return the length of the sequence of characters represented by this
* object.
*/
public int length() {
return value.length;
}
典型的接口注釋(選自JDK 1.8中的String類)
數據成員注釋和接口注釋在大多數情況下都是必須的,這對于讓讀者快速讀懂代碼有很大的幫助。數據成員包括類的普通成員變量和靜態成員變量,數據成員注釋除了描述數據成員的本身用途之外,成員的默認值、副作用等信息都需要提及。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
/**
* Class String is special cased within the Serialization Stream Protocol.
*
* A String instance is written into an ObjectOutputStream according to
* <a href="{@docRoot}/../platform/serialization/spec/output.html">
* Object Serialization Specification, Section 6.2, "Stream Elements"</a>
*/
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
....
}
典型的數據成員注釋(選自JDK 1.8中的String類)
對于一段代碼,如果無法一眼就看出其含義而需要深入分析其實現才能讀懂,就需要添加實現注釋對這段代碼的含義進行說明。代碼的實現注釋通常不是必須的,如果一段代碼每一行都需要注釋,那就要重新審視一下這段代碼的設計是否有合理了。此外,實現注釋描述還需要描述一些代碼無法體現,但是對于讀者了解這段代碼很有幫助的信息,比如代碼的設計思路等。
public final class String {
...
public char[] toCharArray() {
// Cannot use Arrays.copyOf because of class initialization order issues
char result[] = new char[value.length];
System.arraycopy(value, 0, result, 0, value.length);
return result;
}
...
}
典型的實現注釋(選自JDK 1.8中的String類)
模塊依賴注釋相對少見一些,主要描述兩個相互依賴的模塊的一些依賴信息,因為它并不屬于單獨某個模塊,所以在哪里寫這個注釋需要認真衡量一下。實在找不到好的位置時,可以寫一個類似README的文件,專門用于描述模塊之間的依賴關系。
typedef enum Status {
STATUS_OK = 0,
STATUS_UNKNOWN_TABLET = 1,
STATUS_WRONG_VERSION = 2,
...
STATUS_INDEX_DOESNT_EXIST = 29,
STATUS_INVALID_PARAMETER = 30,
STATUS_MAX_VALUE = 30,
// Note: if you add a new status value you must make the following
// additional updates:
// (1) Modify STATUS_MAX_VALUE to have a value equal to the
// largest defined status value, and make sure its definition
// is the last one in the list. STATUS_MAX_VALUE is used
// primarily for testing.
// (2) Add new entries in the tables "messages" and "symbols" in
// Status.cc.
// (3) Add a new exception class to ClientException.h
// (4) Add a new "case" to ClientException::throwException to map
// from the status value to a status-specific ClientException
// subclass.
// (5) In the Java bindings, add a static class for the exception
// to ClientException.java
// (6) Add a case for the status of the exception to throw the
// exception in ClientException.java
// (7) Add the exception to the Status enum in Status.java, making
// sure the status is in the correct position corresponding to
// its status code.
}
典型的模塊依賴注釋(選自《A Philosophy of Software Design》中的例子)
注釋模板為注釋寫作提供了極大的便利,我們常用的開發工具如IDEA、VS Code都對注釋模板有很好的支持。在配置好注釋模板之后(一般情況下默認模板即可),只需簡單的操作,就能生成模板,我們只需往模板里填上內容即可。對于方法的接口注釋,注釋模板尤為方便,方法的入參和返回值標識注釋模板都已經提供好。
/**
*
* @param user
* @param password
* @return
*/
private boolean login(String user, String password) {
...
}
IntelliJ IDEA默認的注釋模板
注釋與代碼重復是開發者最容易犯的一個錯誤,這也是很多開發者認為注釋是冗余的原因。確實,對于那些通過讀代碼可以很容易就推斷出來的信息,注釋就是多余的,更甚者,出現過期注釋還容易誤導讀者。
// Add a horizontal scroll bar
hScrollBar = new JScrollBar(JScrollBar.HORIZONTAL);
add(hScrollBar, BorderLayout.SOUTH);
// Add a vertical scroll bar
vScrollBar = new JScrollBar(JScrollBar.VERTICAL);
add(vScrollBar, BorderLayout.EAST);
// Initialize the caret-position related values
caretX = 0;
caretY = 0;
caretMemX = null;
典型的重復代碼的注釋(選自《A Philosophy of Software Design》中的例子)
某些程序員喜歡在注釋中使用代碼中的變量名或其中的單詞,這種情況也是注釋重復代碼的一種體現。像下面的這個例子,注釋完全就是冗余的,可以猜測開發者純粹是為了注釋而注釋。
/**
* 用戶登錄
* @param user User對象
* @param password Password對象
* @return
*/
private boolean login(User user, Password password) {
...
}
使用代碼變量名的注釋
在進行系統設計時,我們常常會采用分層架構,每一層負責不同功能。系統的頂層往往會更抽象一點,為功能調用者隱藏了很多細節(high-level);底層往往會更細節一點,實現系統的具體功能(low-level)。在進行注釋寫作時,我們也要學會對注釋進行分層。high-level的注釋要提供比代碼更抽象的信息,比如代碼的設計思路;low-level的注釋要提供比代碼更細節的信息,比如表示一個范圍的兩個參數是左閉右開還是左閉右閉;我們需要避免寫出與代碼同一level的注釋,因為這往往就是上一節所提到的注釋重復代碼。
對于需要通過閱讀這一段代碼才能獲取的信息,就適合以low-level注釋寫在這段代碼前面,一些常見的例子有:
low-level注釋不能太過抽象,否則就失去了其應有的作用:
// Current offset in resp Buffer
uint32_t offset;
// Contains all line-widths inside the document and
// number of appearances.
private TreeMap<Integer, Integer> lineWidths;
太過抽象的low-level注釋(選自《A Philosophy of Software Design》中的例子)
好的low-level注釋應當詳細介紹代碼的意圖及其需要注意的點:
// Position in this buffer of the first object that hasn't
// been returned to the client.
uint32_t offset;
// Holds statistics about line lengths of the form <length, count>
// where length is the number of characters in a line (including
// the newline), and count is the number of lines with
// exactly that many characters. If there are no lines with
// a particular length, then there is no entry for that length.
private TreeMap<Integer, Integer> numLinesWithLength;
典型的low-level注釋(選自《A Philosophy of Software Design》中的例子)
high-level注釋的作用是隱藏具體實現細節,快速讓讀者對整一段代碼有一個全局的了解。high-level注釋除了描述對代碼進行抽象的概括(What)之外,還經常描述代碼的設計思路(Why),這有助于幫助讀者更容易、深入地了解整個系統。high-level注釋不應該描述具體代碼實現的信息,更不能犯注釋重復代碼的錯誤。在寫high-level注釋之前,先問一下自己:
太過細節的high-level注釋其實就是在重復代碼,另一方面該注釋既不能解釋此代碼的總體目的,也不能解釋其如何適合包含此代碼的方法,導致其失去了其應有作用:
// If there is a LOADING readRpc using the same session
// as PKHash pointed to by assignPos, and the last PKHash
// in that readRPC is smaller than current assigning
// PKHash, then we put assigning PKHash into that readRPC.
int readActiveRpcId = RPC_ID_NOT_ASSIGNED;
for (int i = 0; i < NUM_READ_RPC; i++) {
if (session == readRpc[i].session
&& readRpc[i].status == LOADING
&& readRpc[i].maxPos < assignPos
&& readRpc[i].numHashes < MAX_PKHASHES_PERRPC) {
readActiveRpcId = i;
break;
}
}
太過細節的high-level注釋(選自《A Philosophy of Software Design》中的例子)
更好的high-level注釋應該在更高級別上描述代碼的整體功能:
// Try to append the current key hash onto an existing
// RPC to the desired server that hasn't been sent yet.
int readActiveRpcId = RPC_ID_NOT_ASSIGNED;
for (int i = 0; i < NUM_READ_RPC; i++) {
if (session == readRpc[i].session
&& readRpc[i].status == LOADING
&& readRpc[i].maxPos < assignPos
&& readRpc[i].numHashes < MAX_PKHASHES_PERRPC) {
readActiveRpcId = i;
break;
}
}
典型的high-level注釋(選自《A Philosophy of Software Design》中的例子)
接口注釋是用的最多的一種注釋,它可以讓讀者快速了解整個模塊的功能并知道如何使用該接口,而不必花費大量時間去閱讀源碼。
對于一個類的接口注釋,其通常是high-level的注釋,主要描述這個類提供的一些功能;
對于一個方法的接口注釋,則同時需要包括high-level和low-level的注釋,常見的有以下幾點:
寫實現注釋時需要記住的最重要的一點就是,描述代碼是做什么的(what)和為什么這么做(why),而不是描述怎么做(how)。因為實現代碼本身就是how,如果還添加描述how的注釋,那就犯了“注釋重復代碼”的錯誤了。
前文中的這個注釋就是典型的描述how的注釋,該注釋除了用文字重復一遍代碼的邏輯,并沒有其他更好的用處:
// If there is a LOADING readRpc using the same session
// as PKHash pointed to by assignPos, and the last PKHash
// in that readRPC is smaller than current assigning
// PKHash, then we put assigning PKHash into that readRPC.
典型的描寫how的實現注釋(選自《A Philosophy of Software Design》中的例子)
更好的方法是描述what的注釋,通過文字表達該段代碼的用途,置于該用途是如何實現的,那就交給代碼去解釋吧!
// Try to append the current key hash onto an existing
// RPC to the desired server that hasn't been sent yet.
典型的描寫what的實現注釋(選自《A Philosophy of Software Design》中的例子)
需要注意的是,實現注釋通常不是必須的,對于一些簡短的、功能單一的函數,一個好的函數命名即可替代實現注釋。
注釋是軟件開發過程中的性價比極高的一個工具,它只需要花費20%的時間,即可獲取80%的價值。代碼注釋最重要的作用就是讓讀者可以在不讀源碼的情況下,快速了解一段代碼的主要功能。注釋的作用還遠遠不止這些,John Ousterhout在《A Philosophy of Software Design》一書中提到,寫注釋最好的時機是在寫代碼之前。這樣,注釋就相當于一種設計工具:在編碼前通過注釋描述這段代碼的功能,然后在通過編碼實現這個功能,如果這個過程有偏差,則再調整注釋。寫注釋時要切記兩點,不要重復代碼!更新代碼后也要更新注釋!前者可以減少冗余的注釋,后者則可以避免因為過期的注釋而誤導讀者。
當然,注釋也不是越多越好,如果一個系統寫的注釋太多,很有可能就是該系統設計得過于復雜,只能通過注釋來幫助讀者理解整個系統。
總而言之,學會怎么寫好代碼注釋,是一個程序員的必備技能,這即是為了開發團隊的其他成員,也是為了未來的自己。
我是一名Java程序員,辭職后全職在做線上一對一Java輔導學習,另外一直在學習如何寫文章,說實在的,每次在后臺看到一些讀者的回應都覺得很欣慰,為了感謝讀者們,我想把我收藏的一些Java干貨貢獻給大家,回饋每一個讀者,希望能幫到你們。
干貨主要有:
① Java電子書(主流和經典的書籍應該都有了)
② Java基礎視頻教程(適合小白學習)
③ Java項目練習
④ Java開發工具,經驗文章
⑤ Java學習路線圖
如果你用得到的話可以直接拿走,私信發送“編程”即可!
avaScript 注釋可用于提高代碼的可讀性。
JavaScript 不會執行注釋。
我們可以添加注釋來對 JavaScript 進行解釋,或者提高代碼的可讀性。
單行注釋以 // 開頭。
代碼練習:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript 注釋</title>
</head>
<body>
<h1 id="q"></h1>
<p id="w"></p>
<script>
// 輸出標題:
document.getElementById("q").innerHTML="Welcome to my Homepage";
// 輸出段落:
document.getElementById("w").innerHTML="This is my first paragraph.";
</script>
</body>
</html>
運行結果:
為前端開發的基石,JavaScript編程語言不僅易學易用,還具有強大的跨平臺特性。本文將為你詳細介紹JavaScript的引入方式、注釋以及輸入輸出,幫助你輕松掌握JavaScript編程的精髓,成為一名炙手可熱的前端開發高手!
作為JavaScript編程語言的初學者,了解如何引入JavaScript代碼是你進入這個神奇世界的第一步。有三種常見的引入方式:
<!DOCTYPE html>
<html>
<head>
<title>內聯方式</title>
</head>
<body>
<h1>你好,謐夜星球!</h1>
<script>
// 在這里編寫你的JavaScript代碼
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>內部文件引入方式</title>
<script src="script.js"></script>
</head>
<body>
<h1>你好,謐夜星球!</h1>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>外部文件引入方式</title>
<script src="script.js"></script>
</head>
<body>
<h1>你好,謐夜星球!</h1>
</body>
</html>
在編程過程中,合理使用注釋可以方便自己和他人理解代碼的邏輯和用途,是良好編程風格的重要一環。在JavaScript中,有兩種注釋方式:
// 這是一個單行注釋
/*
這是一個多行注釋,
可以跨越多行
*/
通過合理注釋,你不僅能提高自己的代碼理解和維護效率,還能方便團隊協作,使代碼更加易于管理。
在前端開發中,與用戶進行互動是至關重要的。JavaScript提供了多種輸入輸出的方式,使得與用戶的交互變得簡單而靈活。
alert("你好,謐夜星球!");
var name = prompt("請輸入用戶名:");
alert("你好, " + name + "!");
console.log("你好,謐夜星球!");
document.getElementById("output").innerHTML = "你好,謐夜星球!";
通過合理運用輸入輸出的方式,你可以實現與用戶的互動,接收用戶的輸入并輸出相應的結果,增加頁面的交互性和用戶體驗。
總結:通過本文的介紹,我們已經了解了JavaScript編程語言的引入方式、注釋以及輸入輸出。作為前端開發的基石,JavaScript在網頁開發中扮演著重要的角色。希望通過學習JavaScript,能夠輕松掌握前端開發的精髓,成為一名炙手可熱的前端開發高手!加油!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。