JSON.stringify()與JSON.parse()沒有你想的那樣簡單

南風晚來晚相識發表於2023-04-17

重新學習這兩個API的起因

在本週五有線上的專案,16:30開始驗證線上環境。
開始都是順順利利,一帆風順。
大概17:50左右,我正在收拾東西。
準備下班去王者峽谷騎著我的船溜達一圈。
可是天降意外,給我派了一個bug。
測試給我說:有一條資料的詳情頁有資料但是在頁面中沒有顯示資料。
不可能,絕對不可能。當時我信誓旦旦的。蠻自信。
當時懷疑是這條資料本身就沒有詳細資料。使用者還沒有補充詳情。
但是測試給我發了一張圖片。
我看見控制檯出現紅色的 Uncaught SyntaxError
映入我的眼球,感覺就像在向我宣戰:
此時我虛了,感覺十有八九就是一個bug。
後來經過排查,發現是 JSON.string()引起的。
故而,今天週六簡單記錄一下 JSON.string它並不是我們想的那樣簡單。

大家對 JSON.string() 的第一印象是什麼?

我現在依稀記得:
JSON.stringify() 方法將一個 JavaScript 物件或值轉換為 JSON 字串。
其他的就沒有什麼特別的印象。
其實,它在對不同型別資料處理時,會做出不同的反應。
下面坐好我在峽谷買的船,一起來看一下。

JSON.string()轉換的值中有 toJSON() 方法,那麼返回值直接替代當前這個物件

var obj = {
  name:'小魔神',
  like:'喜歡和烏鴉說話',
  toJSON: function () {
      return '活下去';
  }
};
var newStr = JSON.stringify(obj);
console.log(newStr);
此時,你認為輸出的值是什麼?
認真考慮10s。是什麼?
最後會輸出  '活下去'
是不是很意外,是不是很驚喜。竟然是這個結果。
這的是我們都沒有想到對吧?
子所以這這個結果:
因為:obj這個物件中有  toJSON()方法。
那麼這個方法的返回值將會替代當前這個物件。所以是 '活下去'

有 toJSON() 方法沒有返回值會怎麼樣?

有的小夥伴這個時候就在想了。
你說的是因為轉換中有 toJSON()方法並且有返回值(retuen)才會替代當前的物件。
如果有 toJSON()方法但是沒有返回值是不是就不會替換當前這個物件了呢?
於是我們寫下了這樣的程式碼
let obj = {
  name: '小魔神',
  like: '喜歡和烏鴉說話',
  toJSON: function() {
    console.log('我沒有返回值')
  }
};
let newStr = JSON.stringify(obj);
console.log(newStr);

大家覺得是輸出什麼結果?
思考5s鍾,你覺得是啥?
輸出 undefined。為什麼是undefined呢?
因為函式沒有返回值的時候,預設返回 undefined
也就是說:
toJSON: function() {
  return undefined
  console.log('我沒有返回值')
}
你以為 JSON.stringify 的神奇之處只有這點。
那你就錯了,它有很多我們之前可能沒有了解的地方。
我們接著往下看,看看還有什麼什麼黑魔法

無法序列化錯誤物件,錯誤物件將會被轉為為空物件

// 建立了一個錯誤物件
const err = new Error('錯的不是我,而是這個世道。')
let obj = {
		name:'小魔神',
		like:'喜歡和烏鴉說話',
		err
}; 
console.log(JSON.stringify(obj));
// 我們發現 err 這個錯誤物件變為了空物件 {}
是不是覺得 JSON.stringify 有點東西在裡面了
我們繼續往下看

物件中不可列舉的值將不會對其序列化

let obj = {
	name:'小魔神',
	like:'喜歡和烏鴉說話',
}; 
Object.defineProperty(obj, 'name', {
	value: '魔神',
	enumerable: false // 將它設定為不可列舉
})
// name屬性將不會被輸出。[因為不會對它進行器序列化]
console.log(JSON.stringify(obj)); 
---這裡可以寫一
是不是覺得 JSON.stringify 越來越有意思了。

NaN 和 Infinity 及 null 都會被當做 null

// 1.7976931348623157E+10308 是浮點數的最大上限值 顯示為Infinity
// -1.7976931348623157E+10308 是浮點數的最小下限值 顯示為-Infinity
const obj = {
    infinityMax: 1.7976931348623157E+10308,
    infinityMin: -1.7976931348623157E+10308,
		a: NaN
}
console.log('obj輸出的值是:', JSON.stringify(obj)); 

日期物件將會對其序列化為字串string

const obj = {
    dateTime: new Date(),
    name: '小魔神',
    like: '喜歡和烏鴉說話',
}
const objCopy = JSON.parse(JSON.stringify(obj));
// 發現型別是字串
console.log('型別是', typeof objCopy.dateTime)
// 因為是字串就無法呼叫原來日期的getTime時間戳了
console.log(objCopy.dateTime.getTime())

所以在序列化日期物件的時候千千萬萬要注意。
因為它會將日期物件最後變成字串。
從而導致之前的日期方法不能夠呼叫。

迴圈引用的物件將會丟擲錯誤

const obj = {
	name:'小魔神',
	like:'喜歡和烏鴉說話',
	sex:null
}
obj.sex = obj; //我們這裡迴圈引用了,將會報錯
const objCopy = JSON.parse(JSON.stringify(obj));
console.log("objCopy", objCopy)

undefined、函式、symbol值 在不同的場合將會發生不同的反應

undefined、函式[方法]、symbol值在不同的場合,
將會發生不同的''化學反應'。
在物件中,作為Value值的時候,在序列的時候將會忽略。
在物件中,將會被轉化為null。
單獨轉化時,將會變為undefined。

undefined、函式、symbol值,在序列化過程中會被忽略 【出現在非陣列物件的屬性值中時】

let person = Symbol('小魔神');
const obj = {
	person,
	un: undefined,
	funSy: () => { console.log('前端已死') }
}
const objCopy = JSON.parse(JSON.stringify(obj));
console.log("objCopy",objCopy)
我們發現  undefined、函式、symbol值,在序列化過程中會被忽略

undefined、任意的函式、symbol 值將會換成 null(出現在陣列中時)

let person = Symbol('小魔神');
let sayFun = function () { console.log("我太難了") }
let arr =[ undefined, person, sayFun]
const objCopy = JSON.parse(JSON.stringify(arr));
console.log("objCopy",objCopy)

所以在進行複製的時候,需要特別注意一下。
方法[任意的函式]會被丟失。不能呼叫

函式、undefined,symbol 被單獨轉換時,會返回 undefined

let a1 = JSON.stringify(function() {})
let a2 = JSON.stringify(undefined)
let a3 = JSON.stringify(Symbol('小魔神'))
console.log(a1)
console.log(a2)
console.log(a3)

遨遊一圈的感想

我們平時在開發中,更多的是使用JSON.string()和JSON.parse()。
對我們需要的資料進行複製。
在複製的過程中需要注意以上的情況。
否者可能出現翻車。
JSON.string()也單獨用在 get 請求將陣列進行序列化。
這個時候各位小夥伴也需要注意一下。避免一些值丟失或者發生變化
還有就是將資料儲存在localStorage、sessionStorage也會使用JSON.string()
我們也需要注意一下

使用JSON.string() 需要注意的點

1.使用JSON.string() 轉換的值中,如果有 toJSON() 方法,那麼返回值直接代替了當前的這個物件 
2.有 toJSON() 方法沒有返回值會返回 undefined
3.無法序列化錯誤物件,錯誤物件將會被轉為為空物件 
4.物件中不可列舉的值將不會對齊序列化 
5.NaN 和 Infinity 及 null 都會被當做 null。
6.日期物件將會對其序列化為字串string
7.迴圈引用的物件將會丟擲錯誤
8.undefined、任意的函式、symbol 值,在序列化過程中會被忽略【出現在非陣列物件的屬性值中時】
或者被轉換成 null(出現在陣列中時)。
函式、undefined,symbol 被單獨轉換時,會返回 undefined

簡單說下 JSON.parse()

我們之前都在介紹 JSON.string(),我們現在簡單說下 JSON.parse()。
畢竟他們倆是一對好基友
JSON.parse() 方法用於將一個 JSON 字串轉換為物件。
那什麼是 JSON字串呢?
JSON 是一種按照 JavaScript 物件語法的資料格式,這是 Douglas Crockford 推廣的。
雖然它是基於 JavaScript 語法,但它獨立於 JavaScript。
這也是為什麼許多程式環境能夠讀取(解讀)和生成 JSON。
JSON.parse(jsonStr,[function])
引數說明:
jsonStr:必需, 一個有效的 JSON 字串。
function: 可選,一個轉換結果的函式, 將為物件的每個成員呼叫此函式。

JSON需要注意的點事項

1.JSON 是一種純資料格式,它只包含屬性,沒有方法。[或者說方法會被丟失]
也就是說:如果你原來的某一個物件中包含方法,在使用JSON之後,該方法會被丟失的哈~

2.JSON 資料格式為鍵/值對。 
JSON 要求在鍵值對 key 和 屬性名稱value周圍使用雙引號。單引號無效。
否者會報錯的哈。Uncaught SyntaxError  未捕獲的語法錯誤

3.JSON 可以將任何標準合法的 JSON 資料格式化儲存,不只是陣列和物件。
比如,一個單一的字串或者數字或者一個空陣列可以是合法的 JSON 物件。
這一點(第3點)很多人認為與第2點互相矛盾。
第二點不是說的是鍵值對key和value嗎?
怎麼單一的字串和空陣列,數字也可以呢?
其實沒有矛盾,你直接使用 JSON.parse([])這樣肯定是不行的。會出現語法錯誤
但是你先使用 JSON.stringify([]) 然後在使用JSON.parse就可以了

4.在使用 JSON.parse的使用需要注意第一個引數是否是JSON字串。
否者會出現轉化失敗

鍵值對必須使用雙引號進行包裹,否則就會報錯

let jsonStr = "{'name': '張老師', 'sex':'男'}";
let newArr = JSON.parse(jsonStr)
console.log(newArr )
// 上面使用的是單引號,會報錯

// 下面使用的是雙引號--不會報錯
// let jsonStr = '{"a1": "Hello", "b1": "World"}';
// let newArr = JSON.parse(jsonStr)
// console.log(newArr )

ps:鍵值對必須使用雙引號進行包裹這裡還隱含了另外一個意思
就是說 key和value必須要都要有雙引號包裹。否則就會出現語法錯誤

使用 JSON.parse() 必須要符合JSON字串

從上面的理解中,我們知道了使用JSON.parse() 必須要符合JSON字串。
下面的使用 JSON.parse() 將會報錯、
非常重要的點:使用JSON.parse() 必須要符合JSON字串
非常重要的點:使用JSON.parse() 必須要符合JSON字串
非常重要的點:使用JSON.parse() 必須要符合JSON字串
重要的事情說三遍

直接轉換陣列

<script>
	let oldObj= []
	let arr = JSON.parse(oldObj)
	console.log('parse', parse )
</script>
將會報錯 
Uncaught SyntaxError: Unexpected end of JSON input at JSON.parse (<anonymous>)
Uncaught SyntaxError  未捕獲的語法錯誤
因為:使用JSON.parse() 必須要符合JSON字串。
然而oldObj= [] 不是一個字串

非要轉換陣列

let strJSON = JSON.stringify([])
let newObj = JSON.parse(strJSON)
console.log('newObj', newObj ) // 輸出的是 []
我們先使用JSON.stringify([])將它轉化為JSON字串就可以了

JSON.parse() 不允許用逗號作為結尾

JSON.parse("[10, 20, 30, 40, ]");
JSON.parse('{"name1" : "澹臺燼", }');

尾聲

1.JSON 是一種純資料格式,它只包含屬性,沒有方法。
2.JSON 要求在鍵值對 key 和 屬性名稱value周圍使用雙引號。單引號無效。
3.JSON 可以將任何標準合法的 JSON 資料格式化儲存。
如:陣列,物件,單一的字串或者數字
4.JSON.parse() 不允許用逗號作為結尾
特別提醒:在使用 JSON.parse的使用需要注意第一個引數是否是JSON字串。
如果你覺得我寫的還不錯:請我點一個推薦或者打賞。感謝各位大佬

相關文章