1. JSON.parse()
JSON.parser() 是JSON 格式的一個函數, 它用於將object 數據類型轉換成為JSON 數據類型, 這里我們來自己實現一下JSON.parser() 函數.
2. 前置知識
2.1 JSON格式中的數據類型
JSON 格式中, 可以將需要處理數據類型分為以下6類, 注意這里的意思是需要處理的屬性類型有以下6 類, 表示的是數據處理的6 種情況;
真實的數據分類並不是按以下分類的, 這里需要注意.
- 字符串
- 數字
- 布爾值
- null 值
- 符號
- 包含轉義字符的字符串(字符串和包含轉義字符的字符串處理方式不同)
2.2 轉義字符的處理
2.2 判斷對象是否相等
自己實現一個函數, 用於判斷兩個對象是否相等, 實現代碼如下:
// 判斷兩個對象是否相等的函數
const objectEquals = (a, b) => {
// 如果 a, b相等,則返回 true,否則返回 false
// Object.getOwnPropertyNames()方法返回一個由指定對象的所有自身屬性的屬性名(包括不可枚舉屬性但不包括Symbol值作為名稱的屬性)組成的數組
// 換句話來說 Object.getOwnPropertyNames()方法返回的是對象所有 key 組成的數組 list
var aProps = Object.getOwnPropertyNames(a)
var bProps = Object.getOwnPropertyNames(b)
if (aProps.length != bProps.length) {
return false
}
for (var i = 0; i < aProps.length; i++) {
var propName = aProps[i]
if (typeof a[propName] === 'object') {
let judge = objectEquals(a[propName], b[propName])
if (!judge) {
return false
}
} else if (a[propName] !== b[propName]) {
return false
}
}
return true
}
2.3 尋找匹配的字符串
這里匹配的字符串主要是括號的匹配, 理解下面的代碼
// 返回對象 '{' 對應的 '}',返回數組 '[' 對應的 ']'
const getAimIndex = (string, index) => {
let str = string
// breakStr是結束判斷字符
let startStr = str[index]
let breakStr = startStr === '{' ? '}' : ']'
let judgeNum = 0
/*
判斷邏輯:
1. 遍歷從 i = 1 開始
2. if temp = '{' || '[', judgeNum++
3. if temp = '}' || ']', judgeNum--
4. if temp = '}' || ']' && judgeNum === 1, return i
*/
for (let i = index, len = str.length; i < len; i++) {
let temp = str[i]
if (temp === startStr) {
judgeNum++
} else if (temp === breakStr && judgeNum !== 1) {
judgeNum--
} else if (temp === breakStr && judgeNum === 1) {
return i
}
}
// log('judgeNum: ', judgeNum)
}
let str = '{{{}}}'
console.log(getAimIndex(str, 0))
2.4 基礎的遞歸思想
這個實現過程中, 很多內容的實現都需要遞歸去完成, 因此對於遞歸有基本的了解會比較好理解.
理解過程可能不會很費勁, 但是如果自己實現的話, 個人認為如果能夠將 Title2.2 判斷對象是否相等 獨立實現就可以自己實現JSON.parse()
3. 實現流程
3.1 將JSON 字符串解析成為tokens 數組
在生產tokens 數組中, 直觀上, 我們會認為我們只需要將JSON 中有意義的字符串添加在tokens 數組中即可, 但是, 這樣做會存在一個問題:
對於如下代碼
{
"obj1": true,
"obj2": "true"
}
上面代碼中, obj1 obj2 屬性本質上是不一樣的, 一個是String 類型, 一個是Boolean 類型, 而我們從JSON 中讀取的內容類型均為String 類型; 因此為了確保完美解析JSON 數據, 我們必須在tokens 數組中存儲JSON 數據的值類型
同時, 由於我們生成對象的時候必須依賴值的類型, 值本身去生成對象, 因此需要提供一個方法返回值內容
綜上所述, 我們使用面向對象的思想來解決這個問題:
const log = console.log.bind(console)
const TYPE = {
"[object Number]": 1,
"[object String]": 2,
"[object Null]": 3,
"[object Boolean]": 4,
"character": 5,
"escapeCharater": 6
}
const includeEscapeCharacter = function(str) {
let asciiEscape = [0, 39, 34, 92, 10, 13, 11, 9, 8, 12]
for (let i = 0, len = str.length; i < len; i++) {
let temp = str[i].charCodeAt(0)
if (asciiEscape.indexOf(temp) !== -1) {
return true
}
}
return false
}
const dealEscape = (str) => {
let escape = {
b: `\b`,
f: '\f',
n: '\n',
r: '\r',
t: '\t',
'\\': '\\',
'\/': '\/',
'\"': '\"',
"\'": "\'"
}
let result = ''
let string = str
let i = 0
let len = string.length
// log(str)
while (i < len) {
let temp = string[i]
if (temp === '\\') {
let endIndex = i + 1
result += escape[string[endIndex]]
i = endIndex + 1
} else {
result += temp
i++
}
}
return result
}
const getType = function (value) {
if (value === ':' || value === '{' || value === '}' || value === '[' || value === ']') {
return 5
}
if (includeEscapeCharacter(value)) {
return 6
}
let type = Object.prototype.toString.apply(value)
return TYPE[type]
}
class JsonValue {
constructor(value) {
this.value = String(value)
this.type = getType(value)
}
toString() {
if (this.type === 1) {
return Number(this.value)
} else if (this.type === 2) {
return String(this.value)
} else if (this.type === 3) {
return null
} else if (this.type === 4) {
return this.value === 'true'
} else if (this.type === 5) {
return String(this.value)
} else if (this.type === 6) {
return dealEscape(this.value)
}
}
}
const __main = () => {
let a = new JsonValue('a')
let b = new JsonValue(1)
log(a)
log(a.toString())
log(b)
log(b.toString())
}
__main()
tokens 數組中存儲的都是JsonValue 類型的數據;
接下來遍歷JSON 數據添加內容即可
3.2 將tokens 數組拼接成為Object 對象
這個過程就是遍歷tokens 數組, 依照符號(:)的索引index, tokens[index - 1] 為key, tokens[index + 1] 為value; 找到key-value 對, 然后添加到結果即可; 需要注意的是這個過程需要考慮Object 的嵌套, 以及Array中Object 的嵌套, 這里需要使用遞歸去處理
這個過程的核心代碼如下: 根據不同的情況, 處理不同的內容
// 獲取key-value對的value值
const getTokensValue = (index, array) => {
let judge = array[index].value
if (judge === '{') {
let nextIndex = getAimIndex(array, index)
let sliceList = array.slice(index + 1, nextIndex)
return parsedDict(sliceList)
} else if (judge === '[') {
let nextIndex = getAimIndex(array, index)
let sliceList = array.slice(index + 1, nextIndex)
return conversionObj(sliceList)
} else {
return array[index].toString()
}
}
4. code
代碼地址: JSON.parse() 的實現