JSON.stringify馴服循環引用的對象

  • 2020 年 3 月 26 日
  • 筆記

在寫Tab組件持久化功能時:

localStorage.setItem('tabs',JSON.stringify(tabs))

遇到如下的報錯:

看意思應該是產生了循環引用的結構,經查閱資料和實踐做個記錄。

以下是不同瀏覽器對於這個類型錯誤報出錯的形式:

TypeError: cyclic object value (Firefox)  TypeError: Converting circular structure to JSON (Chrome and Opera)  TypeError: Circular reference in value argument not supported (Edge)

舉個例子?:

var circularReference = {otherData: 123};  circularReference.myself = circularReference;

此時到控制台裏面運行一下:

JSON.stringify(circularReference);    // 報錯信息如下  VM685:1 Uncaught TypeError: Converting circular structure to JSON      --> starting at object with constructor 'Object'      --- property 'myself' closes the circle      at JSON.stringify ()      at:1:6

可以看到和我上面說的Vue的例子是類似的。

那如何解決呢?

既然是由於循環引用導致的,那我們可以在發生循環引用的地方給切斷。

那如何切斷呢?

幸好JSON.stringify方法提供了便利,語法如下:

JSON.stringify(value[, replacer[, space]])

replacer可以作為一個函數傳入,且接受key和value作為入參,如下:

JSON.stringiify({},function(key,value){      // do sth  })

那解決方案就有啦:

JSON.stringify(circularReference,function(key,value){      // 這裡的key的判斷條件,就是上面報錯信息里的 property 'xxx' closes the circle,      // 這裡xxx是什麼條件就是什麼      if(key == 'myself'){          return      }      return value  })

如果嫌麻煩還有更簡便的方法:

const getCircularReplacer = () => {    const seen = new WeakSet();    return (key, value) => {      if (typeof value === "object" && value !== null) {        if (seen.has(value)) {          return;        }        seen.add(value);      }      return value;    };  }

運行如下:

JSON.stringify(circularReference, getCircularReplacer());  // {"otherData":123}

再推薦幾個解決類似問題的庫:

  • circular-json (現在只維護,vue-devtools內部也使用它)
  • flatted (上面庫的繼承者)
  • json-stringify-safe
  • cycle.js