您好,登錄后才能下訂單哦!
怎樣編寫高質(zhì)量JavaScript代碼,針對(duì)這個(gè)問題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡(jiǎn)單易行的方法。
首先說一下,代碼是寫給自己或團(tuán)隊(duì)成員看的,良好的閱讀方式是編寫高質(zhì)量代碼的前提條件。這里總結(jié)了四點(diǎn)具體操作方式分享給大家。
不要一會(huì)這樣寫,一會(huì)那樣寫,盡量統(tǒng)一寫法,下面舉例。
// bad function foo(x,y) { return { sum : x + y }; } function bar(m, n){ let ret = m*n return ret; } // good function foo(x, y) { // 適當(dāng)?shù)目崭窀糸_,一般符號(hào)前不添加空格,符號(hào)后添加空格 return { sum: x + y, // 拖尾逗號(hào)是合法的,簡(jiǎn)化了對(duì)象和數(shù)組添加或刪除元素 } // 省略結(jié)束分號(hào),當(dāng)然需要知道如何規(guī)避風(fēng)險(xiǎn) } function bar(m, n) { let ret = m * n return ret }
人為去約定代碼格式,是很不方便的,所以可以借助一些工具進(jìn)行自動(dòng)格式轉(zhuǎn)換,如:prettier 插件(https://prettier.io/)。
魔術(shù)數(shù)字(magic number
)是程式設(shè)計(jì)中所謂的直接寫在程式碼里的具體數(shù)值(如“10”“123”等以數(shù)字直接寫出的值)。雖然程式作者寫的時(shí)候自己能了解數(shù)值的意義,但對(duì)其他程式員而言,甚至作者本人經(jīng)過一段時(shí)間后,都會(huì)很難理解這個(gè)數(shù)值的用途。
// bad setTimeout(blastOff, 86400000) document.onkeydown = function (ev) { if (ev.keyCode === 13) { // todos } } // good const MILLISECONDS_IN_A_DAY = 86400000 const ENTER_KEY = 13 setTimeout(blastOff, MILLISECONDS_IN_A_DAY) document.onkeydown = function (ev) { if (ev.keyCode === ENTER_KEY) { // todos } }
當(dāng)然還有魔術(shù)字符串也是像上面一樣去處理,上面代碼中的常量命名推薦采用下劃線命名的方式,其他如變量、函數(shù)等推薦用駝峰進(jìn)行命名。
其實(shí)減少this的使用頻率也是一樣的道理,當(dāng)代碼中充斥著大量this
的時(shí)候,我們往往很難知道它是誰,需要花費(fèi)很多時(shí)間進(jìn)行閱讀。
// bad class Foo { foo() { this.number = 100 this.el.onclick = function () { this.className = "active" } } } // good class Foo { foo() { let context = this context.number = 100 context.el.onclick = function () { let el = this el.className = "active" } } }
無論是編寫模塊、類、還是函數(shù)都應(yīng)該讓他們各自都只有單一的功能,不要讓他們做過多的事情,這樣閱讀起來會(huì)非常簡(jiǎn)單,擴(kuò)展起來也會(huì)非常靈活。
// bad function copy(obj, deep) { if (deep) { // 深拷貝 } else { // 淺拷貝 } } // good function copy(obj) { // 淺拷貝 } function deepCopy(obj) { // 深拷貝 }
4、減少嵌套層級(jí)
多層級(jí)的嵌套,如:條件嵌套、循環(huán)嵌套、回調(diào)嵌套等,對(duì)于代碼閱讀是非常不利的,所以應(yīng)盡量減少嵌套的層級(jí)。
像解決條件嵌套的問題,一般可采用衛(wèi)語句(guard clause
)的方式提前返回,從而減少嵌套。
// bad function foo() { let result if (isDead) { result = deadAmount() } else { if (isRet) { result = retAmount() } else { result = normalAmount() } } return result } // good function foo() { if (isDead) { return deadAmount() } if (isRet) { return retAmount() } return normalAmount() }
除了衛(wèi)語句外,通過還可以采用短路運(yùn)算、條件運(yùn)算符等進(jìn)行條件語句的改寫。
// bad function foo() { if (isOk) { todo() } let grade if (isAdmin) { grade = 1 } else { grade = 0 } } // good function foo() { isOk && todo() // 短路運(yùn)算 let grade = isAdmin ? 1 : 0 // 條件運(yùn)算符 }
像解決回調(diào)嵌套的問題,一般可采用“async/await
”方式進(jìn)行改寫。
// bad let fs = require("fs") function init() { fs.mkdir(root, (err) => { fs.mkdir(path.join(root, "public", "stylesheets"), (err) => { fs.writeFile( path.join(root, "public", "stylesheets", "style.css"), "", function (err) {} ) }) }) } init() // good let fs = require("fs").promises async function init() { await fs.mkdir(root) await fs.mkdir(path.join(root, "public", "stylesheets")) await fs.writeFile(path.join(root, "public", "stylesheets", "style.css"), "") } init()
除了以上介紹的四點(diǎn)建議外,還有很多可以改善閱讀體驗(yàn)的點(diǎn),如:有效的注釋、避免不同類型的比較、避免生澀的語法等等。
在軟件開發(fā)中,代碼的性能高低會(huì)直接影響到產(chǎn)品的用戶體驗(yàn),所以高質(zhì)量的代碼必然是高性能的。這里總結(jié)了四點(diǎn)具體操作方式分享給大家。
提示:測(cè)試JavaScript
平均耗時(shí),可使用console.time()
方法、JSBench.Me
工具、performance
工具等。
遞歸是一種常見的算法,下面是用遞歸實(shí)現(xiàn)的“求階乘”的操作。
// bad function foo(n) { if (n === 1) { return 1 } return n * foo(n - 1) } foo(100) // 平均耗時(shí):0.47ms // good function foo(n, result = 1) { if (n === 1) { return result } return foo(n - 1, n * result) // 這里尾調(diào)用優(yōu)化 } foo(100) // 平均耗時(shí):0.09ms
“尾調(diào)用”是一種可以重用棧幀的內(nèi)存管理優(yōu)化機(jī)制,即外部函數(shù)的返回值是一個(gè)內(nèi)部函數(shù)的返回值。
很多功能都可以采用JavaScript
內(nèi)置方法來解決,往往內(nèi)置方法的底層實(shí)現(xiàn)是最優(yōu)的,并且內(nèi)置方法可在解釋器中提前執(zhí)行,所以執(zhí)行效率非常高。
下面舉例為:獲取對(duì)象屬性和值的復(fù)合數(shù)組形式。
// bad let data = { username: "leo", age: 20, gender: "male", } let result = [] for (let attr in data) { result.push([attr, data[attr]]) } console.log(result) // good let data = { username: "leo", age: 20, gender: "male", } let result = Object.entries(data) console.log(result)
作用域鏈?zhǔn)亲饔糜蛞?guī)則的實(shí)現(xiàn),通過作用域鏈的實(shí)現(xiàn),變量在它的作用域內(nèi)可被訪問,函數(shù)在它的作用域內(nèi)可被調(diào)用。作用域鏈?zhǔn)且粋€(gè)只能單向訪問的鏈表,這個(gè)鏈表上的每個(gè)節(jié)點(diǎn)就是執(zhí)行上下文的變量對(duì)象(代碼執(zhí)行時(shí)就是活動(dòng)對(duì)象),單向鏈表的頭部(可被第一個(gè)訪問的節(jié)點(diǎn))始終都是當(dāng)前正在被調(diào)用執(zhí)行的函數(shù)的變量對(duì)象(活動(dòng)對(duì)象),尾部始終是全局活動(dòng)對(duì)象。
概念太復(fù)雜的話, 看下面這樣一張圖。
作用域鏈這個(gè)鏈表就是 3(頭部:bar) -> 2(foo) -> 1(尾部:全局),所以查找變量的時(shí)候,應(yīng)盡量在頭部完成獲取,這樣就可以節(jié)省性能,具體對(duì)比如下。
// bad function foo() { $("li").click(function () { // 全局查找一次 $("li").hide() // 再次全局查找一次 $(this).show() }) } // good function foo() { let $li = $("li") // 減少下面$li的作用域查找層級(jí) $li.click(function () { $li.hide() $(this).show() }) }
除了減少作用域鏈查找外,減少對(duì)象屬性的查找也是一樣的道理。
// bad function isNull(arg) { return Object.prototype.toString.call(arg) === "[object Null]" } function isFunction(arg) { return Object.prototype.toString.call(arg) === "[object Function]" } // good let toString = Object.prototype.toString function isNull(arg) { return toString.call(arg) === "[object Null]" } function isFunction(arg) { return toString.call(arg) === "[object Function]" }
有時(shí)候編寫程序時(shí),會(huì)出現(xiàn)很多重復(fù)執(zhí)行的代碼,最好要避免做重復(fù)操作。先舉一個(gè)簡(jiǎn)單的例子,通過循環(huán)找到第一個(gè)滿足條件元素的索引位置。
// bad let index = 0 for (let i = 0, len = li.length; i < len; i++) { if (li[i].dataset.switch === "on") { index = i } } // good let index = 0 for (let i = 0, len = li.length; i < len; i++) { if (li[i].dataset.switch === "on") { index = i break // 后面的循環(huán)沒有意義,屬于執(zhí)行不必要的代碼 } }
再來看一個(gè)計(jì)算“斐波那契數(shù)列”的案例。
// bad function foo(n) { if (n < 3) { return 1 } return foo(n - 1) + foo(n - 2) } foo(40) // 平均耗時(shí):1043ms // good let cache = {} function foo(n) { if (n < 3) { return 1 } if (!cache[n]) { cache[n] = foo(n - 1) + foo(n - 2) } return cache[n] } foo(40) // 平均耗時(shí):0.16ms
這里把遞歸執(zhí)行過的結(jié)果緩存到數(shù)組中,這樣接下來重復(fù)的代碼就可以直接讀取緩存中的數(shù)據(jù)了,從而大幅度提升性能。
畫叉號(hào)的部分就會(huì)走緩存,而不會(huì)重復(fù)執(zhí)行計(jì)算。
除了以上介紹的四點(diǎn)建議外,還有很多可以改善代碼性能的點(diǎn),如:減少DOM操作、節(jié)流處理、事件委托等等。
所謂健壯性的代碼,就是編寫出來的代碼,是可擴(kuò)展、可維護(hù)、可測(cè)試的代碼。這里總結(jié)了四點(diǎn)具體操作方式分享給大家。
很多新語法可彌補(bǔ)之前語法的BUG,讓代碼更加健壯,應(yīng)對(duì)未來。
// bad var a = 1 isNaN(NaN) // true isNaN(undefined) // true // good let a = 1 Number.isNaN(NaN) // true Number.isNaN(undefined) // false
新語法還可以簡(jiǎn)化之前的操作,讓代碼結(jié)構(gòu)更加清晰。
// bad let user = { name: "james", age: 36 } function foo() { let arg = arguments let name = user.name let age = user.age } // good let user = { name: "james", age: 36 } function foo(...arg) { // 剩余參數(shù) let { name, age } = user // 解構(gòu)賦值 }
由于產(chǎn)品需求總是會(huì)有新的變更,對(duì)軟件的可擴(kuò)展能力提出了很高要求,所以健壯的代碼都是可以隨時(shí)做出調(diào)整的代碼。
// bad function foo(animal) { if (animal === "dog" || animal === "cat") { // todos } } function bar(name, age) {} bar("james", 36) // good function foo(animal) { const animals = ["dog", "cat", "hamster", "turtle"] // 可擴(kuò)展匹配值 if (animals.includes(animal)) { // todos } } function bar(options) {} // 可擴(kuò)展任意參數(shù) bar({ gender: "male", name: "james", age: 36, })
當(dāng)函數(shù)產(chǎn)生了除了“接收一個(gè)值并返回一個(gè)結(jié)果”之外的行為時(shí),就產(chǎn)生了副作用。副作用不是說一定是有害的,但是如果在項(xiàng)目中沒有節(jié)制的引起副作用,代碼出錯(cuò)的可能性會(huì)非常大。
建議盡量不要去修改全局變量或可變對(duì)象,通過參數(shù)和return
完成需求。讓函數(shù)成為一種純函數(shù),這樣也可使代碼更容易被測(cè)試。
// bad let fruits = "Apple Banana" function splitFruits() { fruits = fruits.split(" ") } function addItemToCart(cart, item) { cart.push({ item, data: Date.now() }) } // good let fruits = "Apple Banana" function splitFruits(fruits) { return fruits.split(" ") } function addItemToCart(cart, item) { return [...cart, { item, data: Date.now() }] }
當(dāng)項(xiàng)目過于復(fù)雜的時(shí)候,經(jīng)常會(huì)把各種邏輯混在一起,對(duì)后續(xù)擴(kuò)展非常不利,而且還影響對(duì)代碼的理解。所以盡量把相關(guān)的邏輯抽離到一起,進(jìn)行集中式的管理。像React
中的hooks
,Vue3
中的Composition API
都是采用這樣的思想。
// bad export default { name: 'App', data(){ return { searchHot: [], searchSuggest: [], searchHistory: [], }, mounted() { // todo hot // todo history }, methods: { handleSearchSuggest(){ // todo suggest }, handleSearchHistory(){ // todo history } } } } // good export default { name: "App", setup() { let { searchHot } = useSearchHot() let { searchSuggest, handleSearchSuggest } = useSearchSuggest() let { searchHistory, handleSearchHistory } = useSearchHistory() return { searchHot, searchSuggest, searchHistory, handleSearchSuggest, handleSearchHistory, } } } function useSearchHot() { // todo hot } function useSearchSuggest() { // todo suggest } function useSearchHistory() { // todo history }
除了以上介紹的四點(diǎn)建議外,還有很多可以改善代碼健壯性的點(diǎn),如:異常處理、單元測(cè)試、使用TS替換JS等等。
最后總結(jié)一下,如何編寫高質(zhì)量JavaScript
代碼:
關(guān)于怎樣編寫高質(zhì)量JavaScript代碼問題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。