Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
家好,我是前端西瓜哥。今天帶大家來學(xué)習 Prettier。
Prettier 是一款流行的代碼格式化工具。它支持的語言相當多。
它很純粹,就一個代碼格式化工具,并不會做代碼質(zhì)量的檢查(比如聲明了一個未被使用的變量)。
Prettier 會強制使用統(tǒng)一的代碼風格,原理就是解析語言生成 AST 抽象語法樹,然后用自己的一套風格寫回到文件。
Prettier 的優(yōu)點:
但 Prettier 堅持自己的品味,它更希望用戶使用它精心挑選出來的代碼風格,只提供較少的自定義配置規(guī)則。
比如有個 printWidth 的配置(默認值為 80),當一行代碼超過特定字符數(shù)時會對其做拆分換行。這個配置無法關(guān)閉,你必須得設(shè)置一個值。
下面我們就來上手 Prettier。
先是安裝:
yarn add --dev --exact prettier
# 或者是
npm install --save-dev --save-exact prettier
這里我們用了 exact 配置項來鎖定版本號,這是因為不同版本 prettier 的代碼風格可能有細微的不同。prettier 并不保證主版本相同的版本下風格是一致的。
使用命令對項目下所有文件進行格式:
npx prettier --write .
你也可以指定目錄,比如 /src;或是用通配符指定特定的文件,比如 app 目錄下的所有 .test.js 結(jié)尾的文件可以用 app/**/*.test.js。
另外,你可以創(chuàng)建 .prettierignore 文件來指定不需要格式化的文件。如:
# Ignore artifacts:
build
coverage
# Ignore all HTML files:
*.html
如果你想要在保存時格式化,一般都是要用到編輯器的插件。對于 VSCode 來說,你需要安裝一個名為 Prettier 的插件,然后再加上 VSCode 配置(項目下加一個 .vscode/setting.json 文件):
{
"editor.defaultFormatter": "esbenp.prettier-vscode", // 默認格式器改為 prettier
"editor.formatOnSave": true // 開啟 “保存自動格式化”
}
或者你不開啟保存自動格式化,可以在覺得需要的時候右鍵選擇 “Format Document“,或者用快捷鍵。
ESLint 是一種 Linter,能夠分析代碼并準確定位錯誤。它支持 代碼質(zhì)量 以及 代碼風格 的檢查。
代碼質(zhì)量,比如啟用 "no-unused-vars",變量如果聲明卻未被使用會被認為不正確。
代碼風格的能力類似 Prettier,比如 "semi": "error" 表示必須用分號結(jié)尾,對應(yīng) Prettier 的 "semi": true。
Prettier 不會標識哪些地方出問題,在編輯器中用波浪線標出來。我用 ESLint 寫新的功能時,因為代碼是半成品,總能看到一堆的錯誤提示,體驗確實不好。
總的來說,Prettier 只做代碼格式化;ESLint 既能做代碼質(zhì)量檢查,也能做代碼風格檢查和修正。
一般來說,項目最好加上 ESLint,這對我們改善代碼質(zhì)量很有幫助。對于代碼格式化,我們可以用 ESLint 或是 Prettier。
如果想用 Prettier 格式化 JS,你需要在 ESLint 配置文件中使用 eslint-config-prettier,將 eslint 中和 prettier 沖突的規(guī)則關(guān)閉。否則你會看到代碼被格式化了兩次,總是會不符合其中一方的規(guī)則。
另外,ESLint 不支持格式化 CSS,還是有必要裝上 Prettier 或 stylelint 的。
TypeScript 團隊在實現(xiàn) typescript-eslint 時,認為 ESLint 不應(yīng)該做代碼格式化,而應(yīng)該是一個真正的只檢查錯誤的 Linter(可能他們被格式化的實現(xiàn)弄煩了),而像是 Prettier 這類的 Formatter 才應(yīng)該做代碼格式化工作。具體可以看下面這篇文章:
https://typescript-eslint.io/docs/linting/troubleshooting/formatting/
Prettier 是一款代碼格式化工具,開箱即用,默認支持語言眾多,風格優(yōu)美,可以讓我們輕裝上陣。如果你自己做一些小項目,用方便的 Prettier 是不錯的選擇。
我是前端西瓜哥,歡迎關(guān)注我,學(xué)習更多前端知識。
作者:田小波 來源:https://www.cnblogs.com/nullllun/p/8358146.html
. 背景
JSON(JavaScript Object Notation) 是一種輕量級的數(shù)據(jù)交換格式。相對于另一種數(shù)據(jù)交換格式 XML,JSON 有著諸多優(yōu)點。比如易讀性更好,占用空間更少等。在 web 應(yīng)用開發(fā)領(lǐng)域內(nèi),得益于 JavaScript 對 JSON 提供的良好支持,JSON 要比 XML 更受開發(fā)人員青睞。所以作為開發(fā)人員,如果有興趣的話,還是應(yīng)該深入了解一下 JSON 相關(guān)的知識。
本著探究 JSON 原理的目的,我將會在這篇文章中詳細向大家介紹一個簡單的JSON解析器的解析流程和實現(xiàn)細節(jié)。由于 JSON 本身比較簡單,解析起來也并不復(fù)雜。所以如果大家感興趣的話,在看完本文后,不妨自己動手實現(xiàn)一個 JSON 解析器。
好了,其他的話就不多說了,接下來讓我們移步到重點章節(jié)吧。
2. JSON 解析器實現(xiàn)原理
JSON 解析器從本質(zhì)上來說就是根據(jù) JSON 文法規(guī)則創(chuàng)建的狀態(tài)機,輸入是一個 JSON 字符串,輸出是一個 JSON 對象。一般來說,解析過程包括詞法分析和語法分析兩個階段。詞法分析階段的目標是按照構(gòu)詞規(guī)則將 JSON 字符串解析成 Token 流,比如有如下的 JSON 字符串:
{ "name" : "小明", "age": 18 }
結(jié)果詞法分析后,得到一組 Token,如下:
{、 name、 :、 小明、 ,、 age、 :、 18、 }
詞法分析解析出 Token 序列后,接下來要進行語法分析。語法分析的目的是根據(jù) JSON 文法檢查上面 Token 序列所構(gòu)成的 JSON 結(jié)構(gòu)是否合法。
比如 JSON 文法要求非空 JSON 對象以鍵值對的形式出現(xiàn),形如 object={string : value}。如果傳入了一個格式錯誤的字符串,比如
{ "name", "小明" }
那么在語法分析階段,語法分析器分析完 Token name后,認為它是一個符合規(guī)則的 Token,并且認為它是一個鍵。
接下來,語法分析器讀取下一個 Token,期望這個 Token 是 :。但當它讀取了這個 Token,發(fā)現(xiàn)這個 Token 是,,并非其期望的:,于是文法分析器就會報錯誤。
這里簡單總結(jié)一下上面兩個流程,詞法分析是將字符串解析成一組 Token 序列,而語法分析則是檢查輸入的 Token 序列所構(gòu)成的 JSON 格式是否合法。這里大家對 JSON 的解析流程有個印象就好,接下來我會詳細分析每個流程。
2.1 詞法分析
在本章開始,我說了詞法解析的目的,即按照“構(gòu)詞規(guī)則”將 JSON 字符串解析成 Token 流。請注意雙引號引起來詞--構(gòu)詞規(guī)則,所謂構(gòu)詞規(guī)則是指詞法分析模塊在將字符串解析成 Token 時所參考的規(guī)則。
在 JSON 中,構(gòu)詞規(guī)則對應(yīng)于幾種數(shù)據(jù)類型,當詞法解析器讀入某個詞,且這個詞類型符合 JSON 所規(guī)定的數(shù)據(jù)類型時,詞法分析器認為這個詞符合構(gòu)詞規(guī)則,就會生成相應(yīng)的 Token。
這里我們可以參考http://www.json.org/對 JSON 的定義,羅列一下 JSON 所規(guī)定的數(shù)據(jù)類型:
當詞法分析器讀取的詞是上面類型中的一種時,即可將其解析成一個 Token。我們可以定義一個枚舉類來表示上面的數(shù)據(jù)類型,如下:
在解析過程中,僅有 TokenType 類型還不行。我們除了要將某個詞的類型保存起來,還需要保存這個詞的字面量。所以,所以這里還需要定義一個 Token 類。用于封裝詞類型和字面量,如下:
public class Token { private TokenType tokenType; private String value; // 省略不重要的代碼 }
定義好了 Token 類,接下來再來定義一個讀取字符串的類。如下:
有了 TokenType、Token 和 CharReader 這三個輔助類,接下來我們就可以實現(xiàn)詞法解析器了。
上面的代碼是詞法分析器的實現(xiàn),部分代碼這里沒有貼出來,后面具體分析的時候再貼。先來看看詞法分析器的核心方法 start,這個方法代碼量不多,并不復(fù)雜。其通過一個死循環(huán)不停的讀取字符,然后再根據(jù)字符的類型,執(zhí)行不同的解析邏輯。
上面說過,JSON 的解析過程比較簡單。原因在于,在解析時,只需通過每個詞第一個字符即可判斷出這個詞的 Token Type。比如:
正如上面所說,詞法分析器只需要根據(jù)每個詞的第一個字符,即可知道接下來它所期望讀取的到的內(nèi)容是什么樣的。如果滿足期望了,則返回 Token,否則返回錯誤。
下面就來看看詞法解析器在碰到第一個字符是n和"時的處理過程。先看碰到字符n的處理過程:
private Token readNull() throws IOException { if (!(charReader.next()=='u' && charReader.next()=='l' && charReader.next()=='l')) { throw new JsonParseException("Invalid json string"); } return new Token(TokenType.NULL, "null"); }
上面的代碼很簡單,詞法分析器在讀取字符n后,期望后面的三個字符分別是u,l,l,與 n 組成詞 null。如果滿足期望,則返回類型為 NULL 的 Token,否則報異常。readNull 方法邏輯很簡單,不多說了。
接下來看看 string 類型的數(shù)據(jù)處理過程:
string 類型的數(shù)據(jù)解析起來要稍微復(fù)雜一些,主要是需要處理一些特殊類型的字符。JSON 所允許的特殊類型的字符如下:
\" \ \b \f \n \r \t \u four-hex-digits \/
最后一種特殊字符\/代碼中未做處理,其他字符均做了判斷,判斷邏輯在 isEscape 方法中。在傳入 JSON 字符串中,僅允許字符串包含上面所列的轉(zhuǎn)義字符。如果亂傳轉(zhuǎn)義字符,解析時會報錯。
對于 STRING 類型的詞,解析過程始于字符",也終于"。所以在解析的過程中,當再次遇到字符",readString 方法會認為本次的字符串解析過程結(jié)束,并返回相應(yīng)類型的 Token。
上面說了 null 類型和 string 類型的數(shù)據(jù)解析過程,過程并不復(fù)雜,理解起來應(yīng)該不難。至于 boolean 和 number 類型的數(shù)據(jù)解析過程,大家有興趣的話可以自己看源碼,這里就不在說了。
2.2 語法分析
當詞法分析結(jié)束后,且分析過程中沒有拋出錯誤,那么接下來就可以進行語法分析了。語法分析過程以詞法分析階段解析出的 Token 序列作為輸入,輸出 JSON Object 或 JSON Array。語法分析器的實現(xiàn)的文法如下:
object={} | { members } members=pair | pair , members pair=string : value array=[] | [ elements ] elements=value | value , elements value=string | number | object | array | true | false | null
語法分析器的實現(xiàn)需要借助兩個輔助類,也就是語法分析器的輸出類,分別是 JsonObject 和 JsonArray。
代碼如下:
語法解析器的核心邏輯封裝在了 parseJsonObject 和 parseJsonArray 兩個方法中,接下來我會詳細分析 parseJsonObject 方法,parseJsonArray 方法大家自己分析吧。
parseJsonObject 方法實現(xiàn)如下:
parseJsonObject 方法解析流程大致如下:
上面的步驟并不復(fù)雜,但有可能不好理解。這里舉個例子說明一下,有如下的 Token 序列:
{、 id、 :、 1、 }
parseJsonObject 解析完 { Token 后,接下來它將期待 STRING 類型的 Token 或者 END_OBJECT 類型的 Token 出現(xiàn)。于是 parseJsonObject 讀取了一個新的 Token,發(fā)現(xiàn)這個 Token 的類型是 STRING 類型,滿足期望。
于是 parseJsonObject 更新期望Token 類型為 SEL_COLON,即:。如此循環(huán)下去,直至 Token 序列解析結(jié)束或者拋出異常退出。
上面的解析流程雖然不是很復(fù)雜,但在具體實現(xiàn)的過程中,還是需要注意一些細節(jié)問題。比如:
在 JSON 中,字符串既可以作為鍵,也可以作為值。作為鍵時,語法分析器期待下一個 Token 類型為 SEP_COLON。而作為值時,則期待下一個 Token 類型為 SEP_COMMA 或 END_OBJECT。
所以這里要判斷該字符串是作為鍵還是作為值,判斷方法也比較簡單,即判斷上一個 Token 的類型即可。如果上一個 Token 是 SEP_COLON,即:,那么此處的字符串只能作為值了。否則,則只能做為鍵。
對于整數(shù)類型的 Token 進行解析時,簡單點處理,可以直接將該整數(shù)解析成 Long 類型。但考慮到空間占用問題,對于 [Integer.MIN_VALUE, Integer.MAX_VALUE]范圍內(nèi)的整數(shù)來說,解析成 Integer 更為合適,所以解析的過程中也需要注意一下。
3. 測試及效果展示
為了驗證代碼的正確性,這里對代碼進行了簡單的測試。測試數(shù)據(jù)來自網(wǎng)易音樂,大約有4.5W個字符。為了避免每次下載數(shù)據(jù),因數(shù)據(jù)發(fā)生變化而導(dǎo)致測試不通過的問題。我將某一次下載的數(shù)據(jù)保存在了 music.json 文件中,后面每次測試都會從文件中讀取數(shù)據(jù)。
關(guān)于測試部分,這里就不貼代碼和截圖了。大家有興趣的話,可以自己下載源碼測試玩玩。
測試就不多說了,接下來看看 JSON 美化效果展示。這里隨便模擬點數(shù)據(jù),就模擬王者榮耀里的狄仁杰英雄信息吧(對,這個英雄我經(jīng)常用)。如下圖:
關(guān)于 JSON 美化的代碼這里也不講解了,并非重點,只算一個彩蛋吧。
4. 寫作最后
到此,本文差不多要結(jié)束了。本文對應(yīng)的代碼已經(jīng)放到了 github 上,需要的話,大家可自行下載。(微信不支持外跳,可點擊文末閱讀原文直達)
傳送門:https://github.com/code4wt/JSONParser
這里需要聲明一下,本文對應(yīng)的代碼實現(xiàn)了一個比較簡陋的 JSON 解析器,實現(xiàn)的目的是探究 JSON 的解析原理。JSONParser 只算是一個練習性質(zhì)的項目,代碼實現(xiàn)的并不優(yōu)美,而且缺乏充足的測試。
同時,限于本人的能力(編譯原理基礎(chǔ)基本可以忽略),我并無法保證本文以及對應(yīng)的代碼中不出現(xiàn)錯誤。如果大家在閱讀代碼的過程中,發(fā)現(xiàn)了一些錯誤,或者寫的不好的地方,可以提出來,我來修改。如果這些錯誤對你造成了困擾,這里先說一聲很抱歉。
最后,本文及實現(xiàn)主要參考了一起寫一個JSON解析器和如何編寫一個JSON解析器兩篇文章及兩篇文章對應(yīng)的實現(xiàn)代碼,在這里向著兩篇博文的作者表示感謝。好了,本文到此結(jié)束,祝大家生生活愉快!再見。
參考
一起寫一個JSON解析器
http://www.cnblogs.com/absfree/p/5502705.html
如何編寫一個JSON解析器
https://www.liaoxuefeng.com/article/994977272296736
介紹JSON
http://json.org/json-zh.html
寫一個 JSON、XML 或 YAML 的 Parser 的思路是什么?
www.zhihu.com/question/24640264/answer/80500016
在開發(fā)工作中,我們可能會碰到這樣的需求:需要將某個對象內(nèi)容彈窗顯示或者保存在文件中,這時候如果你直接彈窗的話,很可能就是下面這樣的:
因為很多接口它對參數(shù)有要求,比如只能是字符串之類的。
這時候,就需要我們將對象轉(zhuǎn)換為字符串進行輸出,JSON.stringify() 方法就可以幫我們實現(xiàn)將對象轉(zhuǎn)為字符串的過程。
JSON.stringify() 方法將一個 JavaScript 對象或值轉(zhuǎn)換為 JSON 字符串,如果指定了一個 replacer 函數(shù),則可以選擇性地替換值,或者指定的 replacer 是數(shù)組,則可選擇性地僅包含數(shù)組指定的屬性。
JSON.stringify(value[, replacer [, space]])
一個表示給定值的JSON字符串。
console.log(JSON.stringify({name: "obj"}))
// '{"name": "obj"}'
replacer 參數(shù)可以是一個函數(shù)或者一個數(shù)組。作為函數(shù),它有兩個參數(shù),鍵(key)和值(value),它們都會被序列化。
在開始時, replacer 函數(shù)會被傳入一個空字符串作為 key 值,代表著要被 stringify 的這個對象。隨后每個對象或數(shù)組上的屬性會被依次傳入。
函數(shù)應(yīng)當返回JSON字符串中的value, 如下所示:
有以下對象:
const data=[
{
name: "person1",
sex: 0,
age: 18,
isStudent: true
},
{
name: "person2",
sex: 1,
age: 25,
isStudent: false
},
{
name: "person3",
sex: 0,
age: 15,
isStudent: true
}
]
接下來我們針對這個對象做各種需求實現(xiàn)。
const res=JSON.stringify(data, ["name", "sex"])
console.log(res);
// `[{"name":"person1","sex":0},{"name":"person2","sex":1},{"name":"person3","sex":0}]`
JSON.stringify() 提供了分離出自己需要的那部分數(shù)據(jù)。
const res=JSON.stringify(data, (key, value)=> {
if (key=='sex') {
return ["女", '男'][value];
}
return value;
})
console.log(res);
// `[{"name":"person1","sex":"女","age":18,"isStudent":true},{"name":"person2","sex":"男","age":25,"isStudent":false},{"name":"person3","sex":"女","age":15,"isStudent":true}]`
JSON.stringify() 提供了回調(diào)函數(shù)做一個映射關(guān)系。
const res=JSON.stringify(data, ["name", "sex"],4)
console.log(res);
輸出如下:
這里使用了 4 個空格作為層級縮進。
注意:使用 “\t” 得到的結(jié)果和使用 4 個空格得到的結(jié)果看起來很像,但實際不是一回事。
JSON.stringify() 方法可以通過參數(shù)控制輸出的數(shù)據(jù)和格式,靈活應(yīng)用它會大大提高我們的工作效率。
學(xué)習有趣的知識,結(jié)識有趣的朋友,塑造有趣的靈魂!
我是〖編程三昧〗的作者 隱逸王,我的公眾號是『編程三昧』,歡迎關(guān)注,希望大家多多指教!
你來,懷揣期望,我有墨香相迎! 你歸,無論得失,唯以余韻相贈!
知識與技能并重,內(nèi)力和外功兼修,理論和實踐兩手都要抓、兩手都要硬!
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。