JavaScript基礎系列之函數和方法詳解
一、函數和方法的區別
- 函數(function):函數是帶有名稱和參數的 JavaScript 代碼段,可以一次定義多次調用。
- 方法(method):當將函數和對象寫在一起時,函數就變成瞭“方法”,比如當函數賦值給對象的屬性時,我們便稱其為“方法”。
二、如何寫好一個函數
在 JS 中,除瞭變量,用的最多的應該就是函數瞭,函數是 Javascript 的第一公民。
2.1 命名準確
2.1.1 函數命名
函數的命名需要明確,語義清晰,簡單概括函數的功能。我們不要想著代碼簡短而縮短函數名稱,這並不會提高什麼性能或效率,相反,一個函數名稱若不夠清晰,往往其他人無法理解。
盡量使用動詞,比如:getXxxxx、setXxxxx,動詞在前面,語義就能更加清晰。
2.1.2 參數命名
強調語義化,參數命名讓調用者更清晰的知道該傳入什麼,對應什麼參數。當然,像一些通用命名還是可接受的,像 callback,fn,即使不看註釋,往往我也知道這裡的參數要做什麼,傳什麼。
2.2 函數註釋
/** * 時間格式化工具函數 * * @param { (Date | number) } date - 時間 * @param { string } unit - 轉換格式 */ export const timeFormat = (date: Date | number | string, unit: string) => { if (!date) { return '' } if (typeof date === 'string') return date; if (typeof date === 'number') { date = new Date(date); } const year = date.getFullYear(); const month = date.getMonth() + 1; const day = date.getDate(); const hour = date.getHours(); const minute = date.getMinutes(); const second = date.getSeconds(); if (unit === 'year') return `${year}`; if (unit === 'month') return `${year}-${month}`; if (unit === 'day') return `${year}-${month}-${day}`; if (unit === 'hour') return `${year}-${month}-${day} ${hour}`; if (unit === 'minute') return `${year}-${month}-${day} ${hour}:${minute}`; if (unit === 'second') return `${year}-${month}-${day} ${hour}:${minute}:${second}`; }
2.2.1 參數註釋
/** * 時間格式化工具函數 * * @param { (Date | number) } date - 時間 * @param { string } unit - 轉換格式 */
@param { type } 參數 – 參數解釋:type 表明的是參數的類型,比如 string,number,當有多個參數類型的時候,可以這麼來標識 { (string|string[]) },表示這個參數可以是字符串或者字符串數組。
對象屬性:需要解釋對象的每一個屬性
/** * 將項目分配給員工的函數 * * @param {Object} employee - 項目員工 * @param {string} employee.name - 項目員工的姓名 * @param {string} employee.department - 項目員工的部門 */ Project.prototype.assign = function(employee) { // ... };
可選參數:
/** * 時間格式化工具函數 * * @param { (Date | number | string) } date - 時間 * @param { string } [unit] - 轉換格式 */ export const timeFormat = (date: Date | number | string, unit: string) => { // ... }
默認值:
/** * 時間格式化工具函數 * * @param { (Date | number) } date - 時間 * @param { string } [unit = 'second'] - 轉換格式 */ export const timeFormat = (date: Date | number | string, unit = 'second') => { // ... }
2.3 函數參數
2.3.1 參數默認值
export const timeFormat = (date: Date, unit = 'second') => { // ... }
2.3.2 對象參數
async function printer_proxy_print( html_str: string, file_path: string, device: string | undefined, orientation: number, printer_mode: string, width: number, height: number, scale: number, from: number, to: number, left_offset: number, top_offset: number, pdf_tools: string | undefined, begin_page = 1, end_page = 1, repeat_times = 1, print_type: string ) { // ... }
可以給參數默認值,這樣可以隻傳前面幾個必要的參數,像這樣調用。
async function printer_proxy_print( html_str: string, file_path: string, device = 'pc', orientation = 'xxx', printer_mode = 'xxx', width = 123, height = 123, scale = 123, from = 123, to = 123, left_offset = 123, top_offset = 123, pdf_tools = 123, begin_page = 1, end_page = 1, repeat_times = 1, print_type = 'base64' ) { // ... } await printer_proxy_print(html_str, file_path);
上面的方法看似可行,實際上,當我中間某個參數不一樣的時候,我就需要把這個參數前面的參數都傳一遍。這樣顯然不可行。所以當參數多的時候,我們需要用對象解構的方式傳參。
async function printer_proxy_print({ html_str, file_path, device = 'pc', orientation = 'xxx', printer_mode = 'xxx', width = 123, height = 123, scale = 123, from = 123, to = 123, left_offset = 123, top_offset = 123, pdf_tools = 123, begin_page = 1, end_page = 1, repeat_times = 1, print_type = 'base64' }) { // ... } await printer_proxy_print({html_str, file_path});
解構的好處便是我可以隨便傳我想要的某幾個參數,而不用在意順序問題。不過像這麼多參數的函數往往存在問題(具體問題具體分析)。也就是下面提到的參數數量問題。
2.3.3 參數數量
一個函數的參數越少越好,最多不應該超過3個,參數多往往意味著關系多,邏輯交叉相對也就多瞭起來。在進行測試的時候,往往也就很難覆蓋到所有條件,出問題概率也就加大瞭。
參數多的時候,有時候也意味著功能多,就違背瞭 單一功能 的原則。
2.3.4 參數類型防禦
在 TS 開發前,我們不知道用戶會傳什麼東西進來,這時候往往容易產生類型錯誤,又或者,我們想實現兼容,像前面的 timeFormat 函數,我們希望用戶調用的時候,可以是想對 時間對象 格式化,也可以是對 時間戳 格式化,那我們就需要做一個防禦處理。
if (!date) { return '' } if (typeof date === 'string') return date; if (typeof date === 'number') { date = new Date(date); }
不過值得註意的是,即使我們用上瞭 TS,在大多數情況下,我們確實可以避免參數類型問題,但是這並不絕對,因為我們有時候會直接接受 接口 返回的數據。
我們常說,永遠不要相信用戶的輸入,同樣,接口返回的數據我也不信,我們不能保證,後端不會出錯,約定好的參數是數組類型,怎麼空的時候,你給我個 null 呢?
當然這些情況有時候需要去試錯,有時候我們能想到的可能,不要偷懶,給寫上類型判斷吧。
2.4 函數的返回
2.4.1 冪等函數
什麼叫冪等,簡單來說,輸入什麼輸出什麼是固定的,入參決定瞭出參,不管調用多少次,隻要輸入一樣,結果應該保持一樣。
function sum(a: number, b: number) { return a + b; }
冪等函數具有可維護性,相對容易進行單元測試。
2.4.2 純函數
純函數在冪等的條件下,還要求沒有副作用。
const dog = { name: 'puppy', age: 2, weight: 30, } if (!dog.color) { console.log('has no color'); } function addColor(dog) { dog.color = 'white'; } addColor(dog); console.log(dog); // {name: "puppy", age: 2, weight: 30, color: "white"}
可以看到,addColor 函數修改瞭 dog 對象的屬性,也就是產生瞭副作用。
function addColor(dog) { let copyDog = Object.assign({}, dog); copyDog.color = 'white'; return copyDog; }
這樣一來,dog 對象的屬性就不會修改,addColor 函數是純函數。
2.4.3 return null
null 在進行處理的時候相對麻煩,需要進行判斷,導致瞭額外的代碼,應當返回空對象,或者是空數組,或者拋出異常。
函數和方法的區別
1)函數(function)是一段代碼,通過名字來進行調用。它能將一些數據(參數)傳遞進去進行處理,然後返回一些數據(返回值),也可以沒有返回值。
2)方法(method)是通過對象調用的javascript函數。也就是說,方法也是函數,隻是比較特殊的函數。他是和一個對象相關聯。假設有一個函數是fn,一個對象是obj,那麼就可以定義一個method:
obj.method = fn;
obj.method();
3)函數的數據是顯式傳遞
4)方法中的數據是隱式傳遞的;方法和對象相關。
總結
到此這篇關於JavaScript基礎系列之函數和方法的文章就介紹到這瞭,更多相關JavaScript函數和方法內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!