摘要:綜述此文檔為谷歌基于代碼風(fēng)格的完整定義。只有一篇文件遵守了以下規(guī)則的情況下,此文件可以被稱為遵從谷歌代碼風(fēng)格。谷歌命名空間繼承關(guān)系聲明谷歌模塊聲明后可以再聲明命名空間繼承關(guān)系。
1.綜述
此文檔為谷歌基于JavaScript代碼風(fēng)格的完整定義。只有一篇JavaScript文件遵守了以下規(guī)則的情況下,此文件可以被稱為遵從谷歌代碼風(fēng)格。
正如其他谷歌代碼風(fēng)格一樣,本文的跨度不僅包括了代碼格式的美觀,同樣也包括了代碼標(biāo)準(zhǔn)以及慣例。
在本文中,除特別注明之外:
術(shù)語(yǔ)“注釋”一般指的是實(shí)現(xiàn)注釋。我們不會(huì)使用“文檔注釋”,而作為代替,我們使用一般術(shù)語(yǔ)"JS注文(JSDoc)"說(shuō)明在/** … */之中的人類可讀以及機(jī)器可讀的注釋。
當(dāng)用到"必須","必須不","應(yīng)該","不應(yīng)該"以及"可以"的時(shí)候,本文遵從RFC 2119術(shù)語(yǔ)規(guī)則。其中,術(shù)語(yǔ)中的傾向于和避免與應(yīng)該和不應(yīng)該一致,而相應(yīng)的,本文中命令與陳述式的說(shuō)明則與術(shù)語(yǔ)中的必須相一致。
1.2 特別提示文中所有的示例代碼都是非標(biāo)準(zhǔn)的。換句話說(shuō),給出的谷歌代碼風(fēng)格示例代碼都不是基于風(fēng)格下實(shí)現(xiàn)這段代碼的唯一方法。示例代碼中給出的可選格式并不是需要強(qiáng)制執(zhí)行的規(guī)則。
2.源文件基本規(guī)則 2.1 文件名文件名必須小寫,并且不可以含有除了下劃線(_)和破折號(hào)(-)之外的標(biāo)點(diǎn)符號(hào)。請(qǐng)與你的項(xiàng)目風(fēng)格和習(xí)慣保持一致。文件名的后綴必須是.js。
2.2 文件編碼:UTF-8所有源文件都必須在UTF-8編碼下。
2.3 特殊字符 2.3.1 空白字符除了換行符之外,在文件出現(xiàn)的空白字符只能是ASCII中的空格符(0x20)。這也就是說(shuō):
字符串中出現(xiàn)其他空白字符都會(huì)被轉(zhuǎn)義
制表符(Tab)不能用于縮進(jìn)
2.3.2 特殊轉(zhuǎn)義字符任何含有特殊轉(zhuǎn)義字符的字符(", ", , b, f, n, r, t, v)總是優(yōu)先于相應(yīng)的數(shù)字轉(zhuǎn)義字符(如x0a, u000a, u{a})。
永遠(yuǎn)不要使用早已被舍棄的八進(jìn)制轉(zhuǎn)義字符。
對(duì)于剩余的非ASCII碼字符,無(wú)論是實(shí)際編碼字符(例如∞),16進(jìn)制字符或是轉(zhuǎn)義字符(例如u221e),只能在使代碼可讀性和可理解性更好的情況下使用。
提示:在使用轉(zhuǎn)義字符的時(shí)候,或者甚至在使用實(shí)際編碼字符的某些時(shí)候,在后面加上一個(gè)解釋說(shuō)明的注釋會(huì)有助于代碼的優(yōu)雅和可讀性。
例如:
const units = "μs";(最優(yōu)秀的寫法,即使沒(méi)有注釋也很明確清晰) const units = "u03bcs"; // "μs"(允許但是不被推薦) const units = "u03bcs"; // Greek letter mu, "s"(允許但是簡(jiǎn)陋而又易于出錯(cuò)) const units = "u03bcs";(失敗的寫法,使閱讀者難以理解) return "ufeff" + content; // byte order mark(很好的寫法,用轉(zhuǎn)義字符來(lái)表示非輸出字符,后面必要時(shí)會(huì)有注釋)
提示:由于某些程序可能無(wú)法正確處理非ASCII碼字符,所以永遠(yuǎn)不要讓你代碼因此而失去可讀性。
同樣,你的代碼也會(huì)因此而失敗需要修復(fù)。
一個(gè)多帶帶源文件必須依次含有以下信息:
許可與版權(quán)信息(如果需要)
文件總覽JS注文(@fileoverview JSDoc)(如果需要)
谷歌模塊(goog.module)聲明
谷歌引入文件(goog.require)聲明
文件正文
3.1 許可與版權(quán)信息(如果需要)如果文件有許可與版權(quán)信息,必須寫在文件結(jié)構(gòu)的第一層。
3.2 文件總覽JS注文(@fileoverview JSDoc)(如果需要)具體格式見7.5節(jié)
3.3 谷歌模塊(goog.module)聲明所有文件都必須在多帶帶的一行中聲明作為谷歌模塊的名字:含有谷歌模塊命名的那一行不能換行,因此它會(huì)被排除在80列(字符)限制之外。
谷歌模塊命名的全部就是定義一個(gè)命名空間。它作為這個(gè)包的名字(用來(lái)映射該源文件在整個(gè)代碼資源目錄里位置的標(biāo)識(shí)),同樣也可以代表該文件的主要類/枚舉類型/接口。
例如:
*goog.module("search.urlHistory.UrlHistoryService");*3.3.1 模塊的層級(jí)關(guān)系
永遠(yuǎn)不要將一個(gè)命名空間定義為另一個(gè)命名空間的直系子空間。
不被允許的操作:
goog.module("foo.bar"); // "foo.bar.qux" would be fine, though goog.module("foo.bar.baz")*;
命名空間的層級(jí)關(guān)系代表了資源目錄的層級(jí)關(guān)系,因此低級(jí)子空間一定代表了高級(jí)父系資源目錄的子目錄。這也就說(shuō),因?yàn)樵谕粋€(gè)資源目錄里,所以父系命名空間的所有者必須清楚的了解所有的子空間。
3.3.2 谷歌測(cè)試專用聲明(goog.setTestOnly)谷歌模塊聲明后可以再聲明為測(cè)試專用(goog.setTestOnly())。
3.3.3 谷歌命名空間繼承關(guān)系聲明(goog.module.declareLegacyNamespac)谷歌模塊聲明后可以再聲明命名空間繼承關(guān)系(goog.module.declareLegacyNamespace)。(盡量避免)
例如:
goog.module("my.test.helpers"); goog.module.declareLegacyNamespace(); goog.setTestOnly();
谷歌聲明命名空間繼承關(guān)系是用來(lái)簡(jiǎn)易地過(guò)渡傳統(tǒng)面向?qū)ο髮蛹?jí)關(guān)系命名,但是有一些命名上的限制。因?yàn)樽酉的K的命名一定是在父系模塊之后,所以它一定不能是另一個(gè)谷歌模塊的子系或者父系。(如:goog.module("parent);和goog.module("parent.child);同時(shí)存在會(huì)產(chǎn)生安全性問(wèn)題goog.module("parent);goog.module("parent.child.grandchild")也會(huì)有同樣的問(wèn)題
3.3.4 ES6模塊因?yàn)榈侥壳盀橹笶S6模塊的語(yǔ)言還沒(méi)有完全完善,所以現(xiàn)在請(qǐng)不要使用ES6模塊(例如,關(guān)鍵詞export和import)。注意,當(dāng)ES6模塊語(yǔ)言完全完善之后,就可以使用那些模塊了。
3.4 谷歌引入聲明(goog.require)在模塊聲明之后,引入的模塊通過(guò)谷歌引入(goog.require)來(lái)聲明。每個(gè)聲明的引入,都會(huì)分配一個(gè)固定的別名,或者拆分成幾個(gè)固定的別名。在代碼和注釋里,除了聲明引入的時(shí)候,這個(gè)別名是引入模塊的唯一合法指代,引入模塊的全名不能被使用。模塊引入的別名盡可能地和該模塊全名通過(guò)點(diǎn)"."拆分后的最后一部分的名字相一致,但是在能夠避免歧義或者明顯增加代碼可讀性的情況下,也可以加上模塊全名的其他部分。
如果只是因?yàn)橐粋€(gè)模塊的副作用(代碼意義的副作用,而非醫(yī)學(xué)意義,可以理解為"本不應(yīng)有或者用戶意料之外的作用",或者簡(jiǎn)單理解為為了避免編譯器警告[warning]的作用)而引入它,可以不用分配名字,但是在代碼的其他地方不能出現(xiàn)該模塊的全名。
引入聲明先后排序規(guī)則:首先將帶名字的引入聲明按首字母排序列出,然后是解構(gòu)(Destructuring)引入聲明按首字母排序列出,最后再把剩余的引入聲明多帶帶列出(一般是用到副作用的引入模塊)。
提示:沒(méi)有必要完全記住這個(gè)順序,然后嚴(yán)格按照這個(gè)順序排列。你可以根據(jù)你的IDE來(lái)排列你的引入聲明,又是較長(zhǎng)的模塊名或者別名會(huì)違反80字符(列)限制,所以這一行不能換行。換句話說(shuō),引入聲明的那幾行不會(huì)有80字符(列)限制。
例子:
const MyClass = goog.require("some.package.MyClass"); const NsMyClass = goog.require("other.ns.MyClass"); const googAsserts = goog.require("goog.asserts"); const testingAsserts = goog.require("goog.testing.asserts"); const than80columns = goog.require("pretend.this.is.longer.than80columns"); const {clear, forEach, map} = goog.require("goog.array"); /** @suppress {extraRequire} Initializes MyFramework. */ goog.require("my.framework.initialization");
不被允許的寫法:
const randomName = goog.require("something.else"); // 名字不匹配 const {clear, forEach, map} = // 不要換行 goog.require("goog.array"); function someFunction() { const alias = goog.require("my.long.name.alias"); // 必須在頂部(層) // … }3.4.1 谷歌前置聲明(goog.forwardDeclare)
前置聲明不常被用到,但是卻是用來(lái)處理循環(huán)依賴和引用后期加載代碼的有效方法。將所有前置聲明一起寫在引入聲明之后。谷歌前置聲明遵從和谷歌引入聲明一樣的規(guī)則。
3.5 文件的正文文件的正文現(xiàn)在所有依賴文件以后(中間至少隔一行)。
其中可以包括任何模塊內(nèi)部聲明(常亮,變量,類,函數(shù)等等)和已引入的符號(hào)。
術(shù)語(yǔ)解釋:塊狀結(jié)構(gòu)指的是類,函數(shù),方法或者任何被大括號(hào)包住的內(nèi)容里的代碼。值得注意的是,由于5.2小節(jié)和5.3小節(jié)的規(guī)定,數(shù)組和對(duì)象也可以被當(dāng)成塊狀結(jié)構(gòu)。
小提示:推薦使用clang-format工具。JavaScript社區(qū)已經(jīng)成功完成了clang-format對(duì)JavaScript語(yǔ)言的支持,其中也集成了幾位著名代碼開發(fā)者的努力。
所有流程控制結(jié)構(gòu)(例如if, else, for, do, while等等)都需要使用大括號(hào),哪怕其包含的主體代碼只有一條指令。第一條指令的非空塊狀結(jié)構(gòu)必須另起一行。
不被允許的寫法:
if (someVeryLongCondition()) doSomething(); for (let i = 0; i < foo.length; i++) bar(foo[i]);
例外情況:如果指令可以完全地用寫在一行里,那么可以不用大括號(hào)以增加可讀性。以下的例子是流程控制結(jié)構(gòu)里唯一可以不用大括號(hào)和空行的例子:
if (shortCondition()) return;4.1.2 非空區(qū)塊:K&R風(fēng)格
根據(jù)K&R風(fēng)格對(duì)于非空區(qū)塊和非空塊狀結(jié)構(gòu)中大括號(hào)的規(guī)定:
左大括號(hào)(前一個(gè)大括號(hào))之前不空行
左大括號(hào)后新起一行
右大括號(hào)(后一個(gè)大括號(hào))前新起一行
在函數(shù),類,類的方法定義的右大括號(hào)之后新起一行,而在else, catch, while,逗號(hào),分好,右小括號(hào)之后的右大括號(hào)不空行。
例如:
class InnerClass { constructor() {} /** @param {number} foo */ method(foo) { if (condition(foo)) { try { // Note: this might fail. something(); } catch (err) { recover(); } } } }4.1.3 空區(qū)塊:可簡(jiǎn)化
空區(qū)塊和空塊狀結(jié)構(gòu)開始之后可以直接結(jié)束,在{}中間不用任何字符,空格和換行,除非它是多區(qū)塊結(jié)構(gòu)中的一部分(比如if/else/try/catch/finally)。
例如:
function doNothing() {} 不被允許的寫法: if (condition) { // … } else if (otherCondition) {} else { // … } try { // … } catch (e) {}4.2 區(qū)塊縮進(jìn):兩個(gè)空格
每當(dāng)新開一個(gè)區(qū)塊或塊狀結(jié)構(gòu),增加兩個(gè)空格的縮進(jìn)。區(qū)塊結(jié)束之后,縮進(jìn)恢復(fù)到前一級(jí)水平??s進(jìn)對(duì)該區(qū)塊內(nèi)的代碼和注釋同樣有效(見4.2節(jié)的例子)。
4.2.1 數(shù)組聲明:可作為塊狀結(jié)構(gòu)任何數(shù)組都可以按塊狀結(jié)構(gòu)的格式書寫。例如,以下的寫法都是有效(不代表全部寫法):
const a = [ 0, 1, 2, ]; const b = [0, 1, 2]; const c = [0, 1, 2]; someMethod(foo, [ 0, 1, 2, ], bar);
也可以使用其他組合,特別是用來(lái)強(qiáng)調(diào)元素之間的分組,而不是只用來(lái)減少大數(shù)組代碼中的垂直長(zhǎng)度。
4.2.2 對(duì)象聲明:可作為塊狀結(jié)構(gòu)任何對(duì)象都可以按塊狀結(jié)構(gòu)的格式書寫,就像4.2.1節(jié)的例子。例如,以下的寫法都是有效(不代表全部寫法):
const a = { a: 0, b: 1, }; const b = {a: 0, b: 1}; const c = {a: 0, b: 1}; someMethod(foo, { a: 0, b: 1, }, bar);4.2.3 類的聲明
類的聲明(無(wú)論是內(nèi)容聲明[declarations]還是表達(dá)式聲明[expressions])都像塊狀結(jié)構(gòu)一樣縮進(jìn)。在類的方法聲明和類中的內(nèi)容聲明(表達(dá)式結(jié)束時(shí)仍然需要加分號(hào))的右大括號(hào)(后一個(gè)大括號(hào))之后不加分號(hào)。其中可以使用關(guān)鍵字extends,但是不要用@extends的JS注文(JSDoc),除非你繼承了一個(gè)模板類型(templatized type)。
例如:
class Foo { constructor() { /** @type {number} */ this.x = 42; } /** @return {number} */ method() { return this.x; } } Foo.Empty = class {}; /** @extends {Foo4.2.4 函數(shù)表達(dá)式} */ foo.Bar = class extends Foo { /** @override */ method() { return super.method() / 2; } }; /** @interface */ class Frobnicator { /** @param {string} message */ frobnicate(message) {} }
當(dāng)聲明匿名函數(shù)時(shí),函數(shù)正文在原有縮進(jìn)水平上增加兩個(gè)空格的縮進(jìn)。
例子:
prefix.something.reallyLongFunctionName("whatever", (a1, a2) => { // Indent the function body +2 relative to indentation depth // of the "prefix" statement one line above. if (a1.equals(a2)) { someOtherLongFunctionName(a1); } else { andNowForSomethingCompletelyDifferent(a2.parrot); } }); some.reallyLongFunctionCall(arg1, arg2, arg3) .thatsWrapped() .then((result) => { // Indent the function body +2 relative to the indentation depth // of the ".then()" call. if (result) { result.use(); } });4.2.5 Switch語(yǔ)句
就像其他塊狀結(jié)構(gòu),該語(yǔ)句的縮進(jìn)方式也是+2。
開始新的一條Switch標(biāo)簽,格式要像開始一個(gè)新的塊狀結(jié)構(gòu),新起一行,縮進(jìn)+2。適當(dāng)時(shí)候可以用塊狀結(jié)構(gòu)來(lái)明確Switch全文范圍。
而到下一條Switch標(biāo)簽的開始行,縮進(jìn)(暫時(shí))還原到原縮進(jìn)水平。
在break和下一條Switch標(biāo)簽之間可以適當(dāng)?shù)乜找恍小?
例子:
switch (animal) { case Animal.BANDERSNATCH: handleBandersnatch(); break; case Animal.JABBERWOCK: handleJabberwock(); break; default: throw new Error("Unknown animal"); }4.3 表達(dá)式 4.3.1 一個(gè)表達(dá)式一行
每一個(gè)表達(dá)式都要新起一行。
4.3.2 分號(hào)結(jié)尾每個(gè)表達(dá)式都要用分號(hào)結(jié)尾。禁止根據(jù)分號(hào)自動(dòng)插入。
4.4 字符限制:80JavaScript有每行最多80字符的限制。除了下面列出的例外情況之外,每行的字符超過(guò)80個(gè)就會(huì)自動(dòng)換行(具體規(guī)則見4.5節(jié))
例外情況:
該行條件上不支持80字符限制的可能(一個(gè)很長(zhǎng)的url,JS注文或者復(fù)制黏貼來(lái)的shell命令)
谷歌模塊(goog.module)和谷歌引入(goog.require)的聲明(見3.3節(jié)和3.4節(jié))
4.5 自動(dòng)換行(Line-wrapping)術(shù)語(yǔ)解釋:自動(dòng)換行是指將一行的代碼分成幾行。
沒(méi)有固定的自動(dòng)換行方法。通常來(lái)講,對(duì)于同一代碼有好幾種合法的自動(dòng)換行的方法。
注意:雖然官方上自動(dòng)換行的目的是規(guī)避每行的字符限制,但是在不違反字符限制的情況,文件作者也可以根據(jù)自己的判斷來(lái)自動(dòng)換行。
小提示:精簡(jiǎn)方法或者變量可以避免自動(dòng)換行。
自動(dòng)換行的第一要?jiǎng)?wù):在更高語(yǔ)言優(yōu)先級(jí)的位置斷句
更好的寫法:
currentEstimate = calc(currentEstimate + x * currentEstimate) / 2.0f;
不夠優(yōu)秀的寫法:
currentEstimate = calc(currentEstimate + x * currentEstimate) / 2.0f;
在上面的例子中,語(yǔ)言優(yōu)先級(jí)從高到底依次排列:表達(dá)式,除號(hào),函數(shù)調(diào)用,參數(shù),數(shù)字。
運(yùn)算符換行規(guī)則:
請(qǐng)?jiān)谶\(yùn)算符之后換行(注意這和JAVA谷歌代碼風(fēng)格不同)?!?”并不是一個(gè)運(yùn)算符,所以不適用上述規(guī)則。
方法和構(gòu)造函數(shù)之后的左圓括號(hào)不能換行。
逗號(hào)緊跟前面的代碼。
注意:自動(dòng)換行的主要目的是使代碼更清晰,代碼不一定要越短越好。
4.5.2 自動(dòng)換行新的一行縮進(jìn)至少+4除非根據(jù)縮進(jìn)規(guī)則特別規(guī)定之外,自動(dòng)換行后新的一行在原有的縮進(jìn)水平上至少增加4個(gè)空格。
如果自動(dòng)換行了多行,則可以適當(dāng)?shù)恼{(diào)整縮進(jìn)水平。一般來(lái)講,自動(dòng)換行新的一行縮進(jìn)會(huì)基于4的倍數(shù)。只有同一層次的兩行才會(huì)用同樣的縮進(jìn)水平。
在以下情況下會(huì)出現(xiàn)空行:
一個(gè)類或者對(duì)象聲明中的兩個(gè)函數(shù)或者方法之間。
例外情況:類中的兩個(gè)屬性聲明之間(中間沒(méi)有其他代碼)可以選擇性地空一行。這樣做可以對(duì)屬性進(jìn)行邏輯分組。
方法中盡量少用空行的邏輯分組。函數(shù)主體的開頭和結(jié)尾不能空行。
一個(gè)類或者對(duì)象聲明中第一個(gè)方法之前或者最后一個(gè)方法之后可以選擇性地空行(既不推薦也不反對(duì))
文件其他需要的位置(例如3.4節(jié)所寫的)
4.6.2 水平空白(空格)水平空白根據(jù)出現(xiàn)的位置不同分為三類:頭部,尾部,中間。頭部的空白(例如縮進(jìn))在本文的其他部分都已經(jīng)解釋過(guò)了,而尾部的空白禁止出現(xiàn)。
除了其他規(guī)則特別規(guī)定以及常量,注釋,JS注文之外,中間部分的水平空白只可以在下列位置出現(xiàn):
用來(lái)分隔保留關(guān)鍵詞(例如if, for, catch)和之后的左圓括號(hào)(()。
用來(lái)分隔右花括號(hào)(})和保留關(guān)鍵詞(例如else, catch)。
左花括號(hào)({)之前,但是有兩個(gè)例外:
a.數(shù)組字面量中一個(gè)函數(shù)的參數(shù)或者元素是對(duì)象字面量(例如foo({a: [{c:b.}])。
b.模板擴(kuò)張中(例如abc${1 + 2}def。
在二元或者三元運(yùn)算符的兩邊
在逗號(hào)和分號(hào)之后。注意,逗號(hào)和分號(hào)之前不能空格。
在對(duì)象字面量中的冒號(hào)之后。
在標(biāo)識(shí)注釋的雙斜杠(//)的兩邊。這里可以使用多個(gè)空格(但是不是必須的)。
在JS注文(JSDoc)的開始標(biāo)志之后和結(jié)束標(biāo)志的兩邊(比如在簡(jiǎn)易聲明或者造型定義中:this.foo = /** @type {number} */ (ba);*或者function(/** string */ foo) {)。
4.6.3 水平對(duì)齊:不被鼓勵(lì)的用法術(shù)語(yǔ)解釋:水平對(duì)齊是指在標(biāo)記后加空格讓它定位到之前的標(biāo)記的正下方。
這種寫法是允許的,但是谷歌代碼風(fēng)格不鼓勵(lì)這種方法。在用到水平對(duì)齊的時(shí)候也甚至不需要保持使用它。
下面是一個(gè)不使用對(duì)齊和使用了對(duì)齊的例子,而后者是不被鼓勵(lì)的用法。
{ tiny: 42, // this is great longer: 435, // this too }; { tiny: 42, // permitted, but future edits longer: 435, // may leave it unaligned };
注意:對(duì)齊可以增加可讀性,但是對(duì)后續(xù)的維護(hù)增加了困難??紤]到后續(xù)改寫代碼可能只會(huì)該代碼中的一行。修改可能會(huì)導(dǎo)致規(guī)則允許下格式的崩壞。這常常會(huì)錯(cuò)使代碼編寫者(比如你)調(diào)整附近幾行的空格,從而導(dǎo)致一系列的格式重寫。這樣,只是一行的修改就會(huì)有一個(gè)“爆炸半徑”(對(duì)附近代碼的影響)。這么做最多會(huì)讓你做一些無(wú)用功,但是至少是個(gè)失敗的歷史版本,降低了閱讀者的速度,也會(huì)導(dǎo)致一些合并沖突。
4.6.4 函數(shù)參數(shù)本規(guī)則更傾向于把所有函數(shù)的參數(shù)放在函數(shù)名的同一行。如果這么做讓代碼超出了80字符的限制,那么就必須做基于可讀性的自動(dòng)換行。為了節(jié)約空間,最好每行都接近80字符,或者一個(gè)參數(shù)一行來(lái)增加可讀性??s進(jìn)4個(gè)空格。允許和圓括號(hào)對(duì)齊,但是不推薦。
下列就是最常見的函數(shù)參數(shù)對(duì)齊模式:
// Arguments start on a new line, indented four spaces. Preferred when the // arguments don"t fit on the same line with the function name (or the keyword // "function") but fit entirely on the second line. Works with very long // function names, survives renaming without reindenting, low on space. doSomething( descriptiveArgumentOne, descriptiveArgumentTwo, descriptiveArgumentThree) { // … } // If the argument list is longer, wrap at 80. Uses less vertical space, // but violates the rectangle rule and is thus not recommended. doSomething(veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo, tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) { // … } // Four-space, one argument per line. Works with long function names, // survives renaming, and emphasizes each argument. doSomething( veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo, tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) { // … }4.7 分組括號(hào)(Grouping parentheses):推薦的寫法
分組括號(hào)也可以不加,在代碼編寫者或評(píng)定者都覺得這樣不會(huì)使代碼產(chǎn)生歧義而且不會(huì)影響代碼的可讀性的情況下。因?yàn)槲覀儾荒芸隙總€(gè)代碼的閱讀者都記住了運(yùn)算符優(yōu)先表。
在delete, typeof, void, return, throw, case, in, of,或 yield的表達(dá)式之后不要加無(wú)用的圓括號(hào)。
類型造型時(shí)要加圓括號(hào):/ @type {!Foo} / (foo)*。
這一節(jié)講的是實(shí)現(xiàn)注釋的規(guī)則。JS注文規(guī)則請(qǐng)見第7章。
4.8.1 塊注釋風(fēng)格塊注釋遵從上下文一致的縮進(jìn)規(guī)則。它們可以用/* … */ 和 //。對(duì)于多行注釋(/* … */),含有*的一行中的*必須與其他的對(duì)齊,以明確注釋沒(méi)有多出來(lái)的文本。如果方法和值的意義不明確,請(qǐng)?jiān)谥蠹由稀皡?shù)名”注釋。
/* * This is * okay. */ // And so // is this. /* This is fine, too. */ someFunction(obviousParam, true /* shouldRender */, "hello" /* name */);
注釋不要包含在帶星號(hào)或者別的字符的框畫(boxes drawn)中。
不要在實(shí)現(xiàn)注釋中JS注文(/** … */)。
JavaScript有一些曖昧的語(yǔ)言特征(甚至有些危險(xiǎn))。這一章講述了哪些特征可以用,哪些特征不可以用,以及有限制的特征使用。
5.1 局部變量的聲明 5.1.1 使用const和let使用const或let來(lái)聲明局部變量。如果一個(gè)變量不會(huì)改變,默認(rèn)用const,請(qǐng)不要使用var。
5.1.2 一條聲明只聲明一個(gè)變量一條聲明只聲明一個(gè)變量:請(qǐng)不要使用這樣的聲明,例如let a = 1, b = 2;。
5.1.3 聲明時(shí)盡量初始化我們不會(huì)常常在塊狀結(jié)構(gòu)的頭部聲明局部變量。相反,我們會(huì)在第一次使用的附近來(lái)聲明(并初始化)它以減少代碼量。
5.1.4 需要注釋聲明類型在聲明的這一行或者上一行加上JS類型注文
例如:
const /** !Array5.2 數(shù)組字面量 5.2.1 用逗號(hào)結(jié)尾*/ data = []; /** @type {!Array } */ const data = [];
在最后一個(gè)元素和右方括號(hào)之間用逗號(hào)結(jié)尾,并分行。
例如:
const values = [ "first value", "second value", ];5.2.2 不要用變長(zhǎng)數(shù)組構(gòu)造函數(shù)(new)
當(dāng)對(duì)代碼進(jìn)行修改時(shí),這種構(gòu)造函數(shù)很容易出錯(cuò)。
不被允許的寫法:
const a1 = new Array(x1, x2, x3); const a2 = new Array(x1, x2); const a3 = new Array(x1); const a4 = new Array()
第三個(gè)例子會(huì)出現(xiàn)異常:如果x1純數(shù)字,a3就會(huì)是個(gè)共有x1個(gè)undefined元素的數(shù)組,如果x1是個(gè)其他數(shù)字,系統(tǒng)會(huì)拋出異常,如果x1是個(gè)其他類型,a3會(huì)是個(gè)單元素的數(shù)組。
應(yīng)該這么寫:
const a1 = [x1, x2, x3]; const a2 = [x1, x2]; const a3 = [x1]; const a4 = [];
但是用 new Array(length)分配一個(gè)確定長(zhǎng)度的數(shù)組是允許的。
5.2.3 非數(shù)字屬性數(shù)組不要定義或使用非數(shù)字屬性(除了length之外)??梢允褂?em>map或者Object替代。
5.2.4 解構(gòu)賦值解構(gòu)賦值時(shí)可以把數(shù)組字面量放在等式的左邊(比如從單一數(shù)組或者迭代中提取多個(gè)值)。在字面量的最后可以包含一個(gè)rest元素(緊更著...和變量名)。不用的元素應(yīng)該省略。
const [a, b, c, ...rest] = generateResults(); let [, b,, d] = someArray;
解構(gòu)賦值可以作為函數(shù)的參數(shù)(注意這里參數(shù)名可以省略)。在字面量里可以定義默認(rèn)值。
/** @param {!Array=} param1 */ function optionalDestructuring([a = 4, b = 2] = []) { … };
不被允許的寫法:
function badDestructuring([a, b] = [4, 2]) { … };
盡量用對(duì)象字面量來(lái)把多個(gè)值封裝成一個(gè)函數(shù)參數(shù)或者返回值,因?yàn)檫@樣可以給元素命名并且聲明不同類型的元素。
5.2.5 展開運(yùn)算符數(shù)組字面量可以用展開運(yùn)算符(...)來(lái)折疊元素。展開運(yùn)算符用于代替一個(gè)不好的構(gòu)造器Array.prototype。展開運(yùn)算符(...)后面不空格。
例如:
[...foo] // preferred over Array.prototype.slice.call(foo) [...foo, ...bar] // preferred over foo.concat(bar)5.3 對(duì)象字面量 5.3.1 用逗號(hào)結(jié)尾
在最后一個(gè)元素和右方括號(hào)之間用逗號(hào)結(jié)尾,并分行。
5.3.2 不要用Object構(gòu)造器雖然Object構(gòu)造器沒(méi)有Array構(gòu)造器相同的問(wèn)題。但是出于一致性的考慮仍然不允許用Object構(gòu)造器。在這里用對(duì)象字面量({}或者{a: 0, b: 1, c: 2})。
5.3.3 帶引號(hào)鍵值和不帶引號(hào)鍵值不要混用結(jié)構(gòu)體(含有不帶引號(hào)鍵值或符號(hào))和字符文本結(jié)構(gòu)(含有計(jì)算屬性或者帶引號(hào)鍵值),在同一個(gè)對(duì)象字面量中不要混用兩者。
不被允許的寫法:
{ a: 42, // struct-style unquoted key "b": 43, // dict-style quoted key }5.3.4 計(jì)算屬性命名
允許使用計(jì)算屬性(例如{["key" + foo()]: 42}),除非計(jì)算屬性鍵值是個(gè)Symbol類型(比如[]Symbol.iterator]),它會(huì)作為字符文本風(fēng)格(帶引號(hào))。也可以使用枚舉值作為計(jì)算屬性鍵值,但是在同一個(gè)字面量里不能與非枚舉值混用。
5.3.5 方法簡(jiǎn)寫(速記方法)在對(duì)象字面量中方法可以用方法簡(jiǎn)寫({method() {… }}),其中用一個(gè)function或一個(gè)箭向函數(shù)字面量來(lái)代替冒號(hào)。
例如:
return { stuff: "candy", method() { return this.stuff; // Returns "candy" }, };
注意在方法簡(jiǎn)寫或者function中的this指的是對(duì)象字面量本身,但是箭向函數(shù)中的this指向的是對(duì)象字面量外面的域。
例如:
class { getObjectLiteral() { this.stuff = "fruit"; return { stuff: "candy", method: () => this.stuff, // Returns "fruit" }; } }5.3.6 屬性簡(jiǎn)寫(速記屬性)
在對(duì)象字面量中允許屬性簡(jiǎn)寫。
例如:
const foo = 1; const bar = 2; const obj = { foo, bar, method() { return this.foo + this.bar; }, }; assertEquals(3, obj.method());5.3.7 重構(gòu)
在表達(dá)式等式的左邊可以用對(duì)象重構(gòu)模式來(lái)重構(gòu)對(duì)象或者從單個(gè)對(duì)象中提取多個(gè)值。
重構(gòu)可以作為函數(shù)參數(shù),但是應(yīng)該保持盡量簡(jiǎn)潔:不帶引號(hào)的速記屬性。更深層次的嵌套屬性和計(jì)算屬性盡量不要使用。重構(gòu)參數(shù)盡量在左手邊定義默認(rèn)值({str = "some default"} = {}優(yōu)于{str} = {str: "some default"}),然后如果一個(gè)重構(gòu)對(duì)象是它自己,必須默認(rèn)設(shè)為{}。JS注文可以給重構(gòu)參數(shù)一個(gè)名字(不會(huì)用到但是可以被編譯器接受)。
例如:
/** * @param {string} ordinary * @param {{num: (number|undefined), str: (string|undefined)}=} param1 * num: The number of times to do something. * str: A string to do stuff to. */ function destructured(ordinary, {num, str = "some default"} = {})
不被允許的寫法:
/** @param {{x: {num: (number|undefined), str: (string|undefined)}}} param1 */ function nestedTooDeeply({x: {num, str}}) {}; /** @param {{num: (number|undefined), str: (string|undefined)}=} param1 */ function nonShorthandProperty({num: a, str: b} = {}) {}; /** @param {{a: number, b: number}} param1 */ function computedKey({a, b, [a + b]: c}) {}; /** @param {{a: number, b: string}=} param1 */ function nontrivialDefault({a, b} = {a: 2, b: 4}) {};
谷歌模塊引入goog.require也可以重構(gòu),這一行不能換行。整個(gè)代碼無(wú)論多長(zhǎng)都必須放在一行(見3.4節(jié))。
5.3.8 枚舉類型對(duì)象字面量加上@enum注釋之后可以定義枚舉類型。一旦定義完成后,就不能再給枚舉類型增加屬性了。枚舉類型必須保持不變,枚舉類型中的值也不能更改。
/** * Supported temperature scales. * @enum {string} */ const TemperatureScale = { CELSIUS: "celsius", FAHRENHEIT: "fahrenheit", }; /** * An enum with two options. * @enum {number} */ const Option = { /** The option used shall have been the first. */ FIRST_OPTION: 1, /** The second among two options. */ SECOND_OPTION: 2, };5.4 類 5.4.1 構(gòu)造
可以使用構(gòu)造器來(lái)構(gòu)造類。在設(shè)置域或者訪問(wèn)this之前構(gòu)造子類必須調(diào)用super()。接口中不可以調(diào)用構(gòu)造函數(shù)。
5.4.2 域在構(gòu)造器中設(shè)置所有實(shí)例的域(例如除了方法之外的所有屬性)。永不要用@const再指定注釋域。私有域必須帶@private的注釋,名字用下劃線結(jié)尾。不要用類的prototype設(shè)置域。
例如:
class Foo { constructor() { /** @private @const {!Bar} */ this.bar_ = computeBar(); } }
注意:構(gòu)造器構(gòu)造完成之后不要給一個(gè)實(shí)例添加或移除屬性,這樣會(huì)大大降低VM優(yōu)化效果。如果一個(gè)域在定義的時(shí)候沒(méi)有初始化,那應(yīng)該在構(gòu)造器中把它設(shè)為undefined以防止之后類型轉(zhuǎn)變。對(duì)象中增加@struct會(huì)檢查未聲明的屬性不能訪問(wèn)和增加。類默認(rèn)有這個(gè)屬性。
5.4.3 計(jì)算屬性類中只有當(dāng)屬性是Symbol類型的時(shí)候才能使用計(jì)算屬性。不允許使用文本屬性(如5.3.3節(jié)中間定義,也就是帶引號(hào)或者非符號(hào)的計(jì)算屬性)。任何類都可以定義可迭代的[Symbol.iterator]的方法。
注意:使用其他內(nèi)置Symbol的時(shí)候要注意不要被編譯器填充導(dǎo)致其他瀏覽器失效。
在不影響可讀性的情況,我們更推薦使用模型內(nèi)置局部方法,而不是私有靜態(tài)方法。
靜態(tài)方法應(yīng)該只在基類中被訪問(wèn)。靜態(tài)方法不能訪問(wèn)包含本構(gòu)造器或者子類構(gòu)造器(如果這么做,必須在定義含有@nocollapse)創(chuàng)建實(shí)例的變量,也不能在沒(méi)有定義過(guò)該方法的子類中調(diào)用。
不被允許的寫法:
class Base { /** @nocollapse */ static foo() {} } class Sub extends Base {} function callFoo(cls) { cls.foo(); } // discouraged: don"t call static methods dynamically Sub.foo(); // illegal: don"t call static methods on subclasses that don"t define it themselves5.4.5 舊風(fēng)格的類聲明
盡管我們推薦ES6的類,但是有些情況下ES6的類不可用。
例如:
如果存在或者即將存在子類,包括創(chuàng)建子類的框架,就不能立刻轉(zhuǎn)為ES6語(yǔ)法。如果這種情況下類使用了ES6語(yǔ)法,那么它下屬的所有子類都要用ES6語(yǔ)法規(guī)范。
因?yàn)镋S6在調(diào)用super返回值之前無(wú)法訪問(wèn)this實(shí)例,所以在調(diào)用超類構(gòu)造器之前需要this的框架會(huì)出問(wèn)題。
以下規(guī)則仍然使用:適當(dāng)時(shí)應(yīng)當(dāng)使用let,const,默認(rèn)參數(shù)(缺省參數(shù)),rest參數(shù)和箭頭函數(shù)。
可以使用goog.defineClass來(lái)模擬一個(gè)類似ES6的類聲明:
let C = goog.defineClass(S, { /** * @param {string} value */ constructor(value) { S.call(this, 2); /** @const */ this.prop = value; }, /** * @param {string} param * @return {number} */ method(param) { return 0; }, });
另外,盡管goog.defineClass更推薦用新語(yǔ)法代碼,但是也允許使用相對(duì)傳統(tǒng)語(yǔ)法的代碼。
/** * @constructor @extends {S} * @param {string} value */ function C(value) { S.call(this, 2); /** @const */ this.prop = value; } goog.inherits(C, S); /** * @param {string} param * @return {number} */ C.prototype.method = function(param) { return 0; };
如果有超類,應(yīng)在超類構(gòu)造器被調(diào)用之后在本構(gòu)造器中定義實(shí)例的屬性。方法應(yīng)在構(gòu)造器的prototype定義。
正確定義構(gòu)造器的prototype層級(jí)是很困難的。所有,最好使用the Closure Library 中的goog.inherits。
關(guān)鍵詞class可以定義比prototype更清晰和可讀性更好的類。常規(guī)實(shí)現(xiàn)代碼不需要操作這些對(duì)象,盡管這樣做可以定義5.4.5節(jié)中所說(shuō)的@record接口和類。不允許混入和修改嵌入對(duì)象的prototype。
例外:代碼框架(例如Polymer或者Angular)有時(shí)需要用到prototype,以避免求助于更不推薦的工作區(qū)。
例外2:定義接口中的類(見5.4.9節(jié))
請(qǐng)不要使用JavaScript中的getter函數(shù)與setter函數(shù)。它們會(huì)產(chǎn)生潛在的危險(xiǎn)和困難,而且只被部分編譯器支持。建議使用常規(guī)方法來(lái)代替。
例外:當(dāng)使用數(shù)據(jù)封裝的框架(例如Polymer或者Angular),可以少量地使用getter函數(shù)與setter函數(shù)。但是請(qǐng)注意,這些方法只被部分編譯器支持。使用的時(shí)候請(qǐng)?jiān)跀?shù)組或者對(duì)象字面量中加上get foo()或者set foo(value)定義,如果做不到這些,請(qǐng)加上Object.defineProperties。請(qǐng)不要使用會(huì)重命名接口屬性的Object.defineProperty。getter函數(shù)一定不能改變顯狀態(tài)。
不被允許的寫法:
class Foo { get next() { return this.nextId++; } }5.4.8 toString方法的覆蓋
方法toString可以被覆蓋,但是定義的方法一定要能無(wú)副作用的運(yùn)行。
特別值得注意的是,在toString中調(diào)用其他方法有可能導(dǎo)致無(wú)限循環(huán)的異常情況。
接口可以用@interface或@record來(lái)聲明。@record聲明的接口可以被類和對(duì)象字面量顯式實(shí)現(xiàn)(例如通過(guò)@implements),也可以隱式實(shí)現(xiàn)。
在接口中的所有非靜態(tài)方法都必須為空區(qū)塊。在接口主體之后必須定義域作為prototype上留的底。
例如:
/** * Something that can frobnicate. * @record */ class Frobnicator { /** * Performs the frobnication according to the given strategy. * @param {!FrobnicationStrategy} strategy */ frobnicate(strategy) {} } /** @type {number} The number of attempts before giving up. */ Frobnicator.prototype.attempts;5.5 函數(shù) 5.5.1 頂層函數(shù)
需要導(dǎo)出的函數(shù)可以直接定義在exports對(duì)象中,也可以局部定義然后多帶帶導(dǎo)出。我們推薦使用不導(dǎo)出的函數(shù),不要加上@private定義。
例如:
/** @return {number} */ function helperFunction() { return 42; } /** @return {number} */ function exportedFunction() { return helperFunction() * 2; } /** * @param {string} arg * @return {number} */ function anotherExportedFunction(arg) { return helperFunction() / arg.length; } /** @const */ exports = {exportedFunction, anotherExportedFunction}; /** @param {string} arg */ exports.foo = (arg) => { // do some stuff ... };5.5.2 嵌套函數(shù)和閉包
函數(shù)中可以嵌套函數(shù),如果需要給函數(shù)命名,必須局部const定義。
5.5.3 箭頭函數(shù)箭頭函數(shù)語(yǔ)法簡(jiǎn)潔,而且彌補(bǔ)了使用this的很多問(wèn)題。比起使用關(guān)鍵詞function,我們更推薦箭頭函數(shù),特別適用于嵌套函數(shù)(見5.3.5節(jié))。
比起f.bind(this),特別是goog.bind(f, this),我們傾向于使用箭頭函數(shù)。避免const self = this的寫法。箭頭函數(shù)常常對(duì)于有可能會(huì)傳參的回調(diào)很有效。
箭頭的右邊可以是個(gè)表達(dá)式和塊狀結(jié)構(gòu)。如果只有一個(gè)多帶帶的非解構(gòu)參數(shù),那么參數(shù)的圓括號(hào)可以省略。
注意:因?yàn)槿绻笃谠黾雍瘮?shù)的參數(shù),省略圓括號(hào)會(huì)使代碼出錯(cuò),所以即使箭頭函數(shù)只有一個(gè)參數(shù),加上圓括號(hào)也是很好的做法。
生成器函數(shù)提供了一些有用的抽象方法,可以在需要的時(shí)候使用。
定義生成器函數(shù)時(shí),在function后附上*,并與函數(shù)名空一格。當(dāng)使用授權(quán)域的時(shí)候,在yield后加上*。
例如:
/** @return {!Iterator5.5.5 參數(shù)} */ function* gen1() { yield 42; } /** @return {!Iterator } */ const gen2 = function*() { yield* gen1(); } class SomeClass { /** @return {!Iterator } */ * gen() { yield 42; } }
函數(shù)參數(shù)必須在JS注文中預(yù)定義格式化,除非使用了@override,所有類型省略。
內(nèi)聯(lián)參數(shù)類型必須在參數(shù)名前特別說(shuō)明(比如/ number / foo, / string / bar) => foo + bar)。內(nèi)聯(lián)類型注釋和@param類型注釋在同一個(gè)函數(shù)聲明時(shí)不能混用。
在參數(shù)列中可選參數(shù)允許使用等號(hào)操作符。就像必須參數(shù)一樣寫可選參數(shù)(比如不加opt_前綴),等號(hào)兩邊需加空格。在JS注文可選參數(shù)必須在類型聲明中加上=后綴,不要用初始化以確保代碼明確。就算可選參數(shù)的默認(rèn)值是undefined,也要在函數(shù)聲明中聲明默認(rèn)值。
例如:
/** * @param {string} required This parameter is always needed. * @param {string=} optional This parameter can be omitted. * @param {!Node=} node Another optional parameter. */ function maybeDoSomething(required, optional = "", node = undefined) {}
盡量少用默認(rèn)參數(shù)。當(dāng)有較多非自然語(yǔ)序可選參數(shù)時(shí),我們更推薦重構(gòu)(見5.3.7節(jié))來(lái)創(chuàng)建可讀性更好的API。
注意:不同于python的缺省參數(shù),允許使用返回新的可變對(duì)象(比如{}和[])的初始化模塊因?yàn)樗鼤?huì)預(yù)先設(shè)定每次都使用默認(rèn)值,所以調(diào)用之間對(duì)象不會(huì)共享。
小提示:包括函數(shù)調(diào)用的任何表達(dá)式都會(huì)用到初始化模塊,所以初始化模塊應(yīng)該盡量簡(jiǎn)單。避免初始化模塊暴露共享可變域,這容易導(dǎo)致函數(shù)調(diào)用之間的無(wú)意耦合。
用剩余參數(shù)來(lái)代替訪問(wèn)arguments。在JS注文中剩余參數(shù)需要加一個(gè)...前綴。剩余參數(shù)必須在參數(shù)列表的最后。在參數(shù)名和...之間不要空格。不要把它命名成var_args,也不要用arguments來(lái)命名參數(shù)或局部變量,以避免和內(nèi)置名的混淆。
例如:
/** * @param {!Array5.5.6 返回} array This is an ordinary parameter. * @param {...number} numbers The remainder of arguments are all numbers. */ function variadic(array, ...numbers) {}
在函數(shù)聲明之前的JS注文中必須明確定義返回值類型,除非是@override情況下所有類型都省略。
5.5.7 泛型定義泛型函數(shù)或方法時(shí)需在JS注文中加上@template TYPE。
5.5.8 展開運(yùn)算符函數(shù)調(diào)用時(shí)可以使用展開運(yùn)算符(...)。一個(gè)可變函數(shù)中一個(gè)數(shù)組或者它的迭代被分配成了多個(gè)參數(shù)時(shí),比起Function.prototype.apply,我們更推薦展開運(yùn)算符。
例如:
function myFunction(...elements) {} myFunction(...array, ...iterable, ...generator());5.6 字符串字面量 5.6.1 單引號(hào)
通常情況下,比起雙引號(hào),我們更推薦單引號(hào)來(lái)修飾字符串字面量。
小提示:如果字符串中含有單引號(hào),考慮使用模板字符串來(lái)避免解析錯(cuò)誤。
通常情況下,字符串不能跨行。
使用模板字符串來(lái)處理復(fù)雜的字符串拼接,尤其當(dāng)處理多條字符串字面量時(shí)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/92613.html
摘要:剛做完的一個(gè)項(xiàng)目里,為了切圖方便,接觸了下的腳本功能。這里解釋一下,也就是本文所討論的腳本,并不只有可以用,都是可以用的,不過(guò)需要調(diào)用各自不同的。一些腳本的線上參考資料常用部分漢化版常數(shù)表初識(shí)腳本留坑待續(xù) 剛做完的一個(gè)H5項(xiàng)目里,為了切圖方便,接觸了下Photoshop的腳本功能。從找資料、寫腳本到實(shí)際能用全套跑了一圈下來(lái)發(fā)現(xiàn),嗯,果然是挺難用的[捂臉]。不過(guò)雖然缺點(diǎn)滿滿,但PS這個(gè)平...
摘要:在面向?qū)ο蟮恼Z(yǔ)言中,比如,等,單例模式通常是定義類時(shí)將構(gòu)造函數(shù)設(shè)為,保證對(duì)象不能在外部被出來(lái),同時(shí)給類定義一個(gè)靜態(tài)的方法,用來(lái)獲取或者創(chuàng)建這個(gè)唯一的實(shí)例。 萬(wàn)事開頭難,作為正經(jīng)歷菜鳥賽季的前端player,已經(jīng)忘記第一次告訴自己要寫一些東西出來(lái)是多久以的事情了。。。如果,你也和我一樣,那就像我一樣,從現(xiàn)在開始,從看到這篇文章開始,打開電腦,敲下你的第一篇文章(或者任何形式的文字)吧。 ...
原文 先說(shuō)1.1總攬: Reactor模式 Reactor模式中的協(xié)調(diào)機(jī)制Event Loop Reactor模式中的事件分離器Event Demultiplexer 一些Event Demultiplexer處理不了的復(fù)雜I/O接口比如File I/O、DNS等 復(fù)雜I/O的解決方案 未完待續(xù) 前言 nodejs和其他編程平臺(tái)的區(qū)別在于如何去處理I/O接口,我們聽一個(gè)人介紹nodejs,總是...
摘要:前言本文主要是有關(guān)前端方面知識(shí)按照目前的認(rèn)知進(jìn)行的收集歸類概括和整理,涵蓋前端理論與前端實(shí)踐兩方面。 前言:本文主要是有關(guān)前端方面知識(shí)按照 XX 目前的認(rèn)知進(jìn)行的收集、歸類、概括和整理,涵蓋『前端理論』與『前端實(shí)踐』兩方面。本文會(huì)告訴你前端需要了解的知識(shí)大致有什么,看上去有很多,但具體你要學(xué)什么,還是要 follow your heart & follow your BOSS。 初衷...
閱讀 4295·2022-09-16 13:49
閱讀 1430·2021-11-22 15:12
閱讀 1556·2021-09-09 09:33
閱讀 1064·2019-08-30 13:15
閱讀 1763·2019-08-29 15:30
閱讀 709·2019-08-27 10:52
閱讀 2674·2019-08-26 17:41
閱讀 1935·2019-08-26 12:11