天天看點

JS中JSON.stringify序列化和JSON.parse反序列化問題和解決

當我們想把對象轉為JSON串的時候,肯定會想到JSON.stringify;還一種情況,當我們想實作一個深拷貝的時候,也會想到JSON.parse(JSON.stringify());但其實JSON.stringify序列化是有些問題的。先給大家舉個例子:

現在有個對象,通過JSON.stringify轉一下如下: 

JS中JSON.stringify序列化和JSON.parse反序列化問題和解決
轉完之後我們發現,函數和值為undefined的屬性都丢失了,值為NaN的轉為了null ,這就是存在的問題,當我們想實作一個深拷貝的時候,如果對象中沒有函數或者undefined或者上面的問題不影響我們的使用,自然是可以通過這種方式的,又友善,誰不願意用呢。但如果上面的問題有影響到我們正常使用,或者我們就想保留上面的屬性,那就不能使用JSON這種方式,可以手寫一個深拷貝來實作,可以去看下我之前的一篇博文:《js深拷貝》

出現上面的原因是因為: 

  • 使用JSON.Stringify 轉換的資料中,如果包含 function,undefined,Symbol,這幾種類型,不可枚舉屬性,JSON.Stringify序列化後,這個鍵值對會消失。
  • 轉換的資料中包含 NaN,Infinity 值(含-Infinity),JSON序列化後的結果會是null。
  • 轉換的資料中包含Date對象,JSON.Stringify序列化之後,會變成字元串。
  • 轉換的資料包含RegExp 引用類型序列化之後會變成空對象。
  • 無法序列化不可枚舉屬性。
  • 無法序列化對象的循環引用,(例如: obj[key] = obj)。
  • 無法序列化對象的原型鍊。

JSON.stringify自然好用,但是也要避免上面的問題 

針對上面的問題,我想看下能不能解決,然後自己試着寫了一下,當然也找了一些關于JSON的方法、參數等資料,重新寫了兩個方法(算是對stringify和parse方法進行了擴充吧) 如下:

// JSON.stringify對象序列化,解決undefined、函數和NaN 丢失問題
function JSONStringify(option) {
  return JSON.stringify(option, (key, val) => {
    // 處理函數丢失問題
    if (typeof val === 'function') {
      return `${val}`;
    }
    // 處理undefined丢失問題
    if (typeof val === 'undefined') {
      return 'undefined';
    }
    // 處理NaN轉為null的情況(注意: 這裡如果使用isNaN的話,那麼對象也會走進去)
    if (val !== val) {
      return `${val}`
    }
    return val;
  }, 2)
}
// JSON.parse 反序列化
function JSONParse(jsonStr) {
  const retain = ['function', 'undefined', 'NaN']
  return JSON.parse(jsonStr, (key, val) => {
    // eval 可能在eslint中報錯,需要加入下行注釋
    // eslint-disable-next-line
    if (typeof val === 'string' && retain.some(s => val.indexOf(s) >= 0)) {
      return eval(`(function(){return ${val}})()`);
    }
    return val
  })
}
           

然後使用這兩個方法再試一下 

console.log(JSONStringify(o), 'stringify序列化處理----->>>')
console.log(JSONParse(JSONStringify(o)), 'parse解析序列化處理----->>>')
           

列印如下: 

JS中JSON.stringify序列化和JSON.parse反序列化問題和解決

開始我以為萬事大吉了,但是仔細一看,序列化确實沒有問題的,全部保留了,但是解析的時候我的undefined還是沒了,undefined和函數、NaN一樣處理的,因為一個函數沒有傳回值的話,預設傳回的就是undefined,現在我們讓它傳回一個undefined,但是沒出來呢,也可能是JSON.parse确實轉不了undefined,目前還沒找到答案,先這樣吧,至少比之前好多了。 

深拷貝的時候如果不考慮undefined,基本上直接可以用上面的兩個方法了,還是蠻友善的,當然手寫一個深拷貝的工具方法也友善,看自己喜好吧。

注意:上面兩個方法互相結合、互相使用

最後再補充一下,上面有用到eval函數,這是原生js一個函數,可以直接使用,可以參考一位博友的文章:淺談JS中的 eval函數 

繼續閱讀