JSON
是一種輕量級的數據交換格式,它有鍵值對集合(js 中的對象)和數組兩種結構。JSON
是一個通用的格式,在先後端語言中都能跟該 JSON
打交道。css
有時候咱們須要將 JSON
格式輸入至頁面展現的需求,其中還須要保持必定的索引,那麼該如何實現呢?html
咱們將對象轉爲 JSON
字符串時會常常使用 JSON.stringify 這個 API,其實該方法就內置有格式化的參數:git
var userInfo = {name: 'anran758',github: 'https://github.com/anran758'}; var info = JSON.stringify(userInfo, null, 2); console.log(info); // "{↵ "name": "anran758",↵ "github": "https://github.com/anran758"↵}"
在上面的代碼中,咱們第一個參數(value
)傳入了一個須要序列化的對象。第二個參數是replacer
,用以對屬性轉換和處理,因爲咱們不須要額外的處理,所以傳入一個null
;第三個參數則是空格索引的個數,封頂是10
,0
或不傳則沒有空格。github
在控制檯打印出信息後,咱們能夠看的出來格式化的數據是帶換行符,而且有縮進的格式。接下來咱們就要考慮如何輸出到頁面中。面試
只要學過HTML
的朋友都知道,咱們直接將數據輸入至HTML
中,空格縮進會被瀏覽器給忽略掉的。所以不能輸入到<div>
中。這時候又想到,JSON
格式實際上也算是代碼的一種,那能不能輸入至雷士代碼塊的標籤中呢?答案是能夠的。後端
HTML
中有兩個標籤能夠展現源代碼: <pre>
和 <code>
。它們之間不一樣之處在於:數組
<pre>
表示預約義格式文本,按照原文件中的編排,以等寬字體的形式展示出來,文本中的空白符(好比空格和換行符)都會顯示出來。<code>
則是呈現一段計算機代碼,它以瀏覽器的默認等寬字體顯示,但並不必定會完整呈現原來的格式。這些標籤知識實際上算是比較冷門的知識,或許遠古的面試題會考這種知識點,平時不多會遇到。可是若是你常常使用markdown
的話,那麼這些標籤在markdown
中有不一樣的別名:瀏覽器
好比 markdown 語法中的 ``,實際上等同於 <code> 標籤。實際做用是短代碼塊標籤 而 markdown 語法中的長代碼塊就等同於 <pre> 標籤,不一樣的博客或者網站的應用中還能夠對 <pre> 加類名,用以展現不一樣的語言的語法高亮。
經過三者之間的對比能夠看出,只有 <pre>
纔是符合咱們需求的。markdown
肯定好展現的方式後,就能夠考慮進一步擴展格式化的功能。好比對象中還有屬性是 JSON
字符串的話,咱也進一步的解析,直至最底層。想實現這種功能須要編寫一個遞歸函數,考慮以下代碼:
const isPlainObject = (v) => Object.prototype.toString.call(v) === "[object Object]" const isString = (v) => Object.prototype.toString.call(v) === "[object String]" /** * 格式 JSON 字符串爲對象 * * @author anran758 * @param { any } */ function formatJsonStrAsObj(sample) { let temp = sample; if (isString(temp)) { // 由於有解析失敗的可能,使用 try catch 作相應處理 try { temp = JSON.parse(temp); } catch (ex) { // parse error,return this sample return sample; } } if (isPlainObject(temp)) { temp = { ...temp }; Object.keys(temp).forEach(key => { const item = temp[key]; // 字符串或者對象進行遞歸確認 if (isString(item) || isPlainObject(item)) { temp[key] = formatJsonStrAsObj(item); } }); } return temp; } /** * 將 JSON 字符串轉換爲帶縮進的字符串 * * @param {*} sample JSON 字符串 * @param {number} [indnt=2] 縮進數 * @returns */ function formatJSONIndnt(sample, indnt = 2) { const newSample = formatJsonStrAsObj(sample); if (isString(newSample)) return newSample; try { return JSON.stringify(newSample, null, indnt); } catch (ex) { return newSample.toString(); } } const info = JSON.stringify({ name: 'anran758', avatar: 'https://xxx', detail: JSON.stringify({ desc: 'some description', level: 2, }) }) const data = formatJSONIndnt(info); console.log(data); // 能夠直接將 data 輸出至 dom 中
上文講了如何將數據輸出至頁面,以及擴展格式化功能的示例。接下來說解輸入方面的應用。
當用戶從別的地方複製數據想粘貼至輸入框時,能夠在輸入框上設置監控事件,觸發事件後嘗試幫用戶格式化數據,示例代碼以下:
<div class="container"> <pre class="preview pre"></pre> <textarea class="textarea"></textarea> </div>
const info = JSON.stringify({ name: 'anran758', avatar: 'https://xxx', detail: JSON.stringify({ desc: 'some description', level: 2, }) }) const data = formatJSONIndnt(info); const textarea = document.querySelector('.textarea'); const preview = document.querySelector('.pre'); preview.innerHTML = data; textarea.addEventListener('paste', (e) => { // 阻止默認事件 e.preventDefault(); const value = (e.clipboardData || window.clipboardData).getData('text'); // 這裏使用了上面定義的函數,進行格式化數據 e.target.value = formatJSONIndnt(value, 2); })
body { display: flex; margin: 0; justify-content: center; align-items: center; padding: 0 10px; box-sizing: border-box; min-height: 100vh; } .container { display: flex; width: 100%; } .preview { flex: 1; margin-bottom: 20px; padding: 20px; background: #f5fcff; border: 1px solid #d3eeff; border-radius: 3px; margin: 0; } .textarea { flex: 1; margin-left: 20px; padding: 10px; font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; } .preview + .preview { margin-left: 10px; }