1. 程式人生 > 程式設計 >簡易版本JSON.stringify的實現及其六大特性詳解

簡易版本JSON.stringify的實現及其六大特性詳解

目錄
  • 前言
  • ON.stringify六大特性
    • 特性一
    • 特性二
    • 特性三
    • 特性四
    • 特性五
    • 特性六
  • 手動實現stringify
    • 總結

      前言

      JSON.stringify是一個使用非常高頻的API,但是其卻存在一個特性,我們在使用的過程中需要留意這些特性以避免為程式碼程式埋雷,那麼接下來便一起動手實現一個簡易版本的jsonStringify函式

      JSON.stringify六大特性

      特性一

      布林值、數字、字串的包裝物件在序列化過程中會自動轉換成對應的原始值

      現在有這麼一個物件:

      const obj = {
          bol: new Boolean(true),num: new Number(1),str: new String(1)
      }
      

      利用typeof檢測obj各個屬性的資料型別

      typeof obj.bol; // object
      typeof obj.num; // object
      typeof obj.str; // object
      

      將其序列化stringify之後

      JSON.stringify(obj); // {"bol":true,"num":1,"str":"1"}
      

      此時再將其解析parse進行各個屬性的資料型別

      const stringifyObj = JSON.parse(JSON.stringify(obj));
      typeof stringifyObj.bol; // boolean
      typeof stringifyObj.num; // number
      typeof stringifyObj.str; // string
      

      特性二

      NaN、Infinity、-Infinity以及null在序列化stringify時都會被當作null

      const obj = {
          nan: NaN,infinity: Infinity,null: null,};
      
      JSON.stringify(obj); // {"nan":null,"infinity":null,"null":null}
      

      特性三

      物件在序列化的時候,若是其存在toJSON函式,這個函式返回的值就是整個物件序列化後的結果

      const obj = {
          nan: NaN,toJSON() {
              return "擁有toJSON函式";
          },};
      
      JSON.stringify(obj); // "擁有toJSON函式"
      

      可以看到序列化之後的資料僅存在toJSON函式的返回值,其餘資料全部忽略

      ⚠️:Date資料會被正常序列化,因為Date上部署了toJSON函式,可以通過控制檯列印Date.prototype.toJSON得知

      const obj = {
          date: new Date(),};
      
      JSON.stringify(obj); // {"date":"2021-10-08T11:43:31.881Z"}
      

      特性四

      表現不一的undefined、function和symbol

      作為物件鍵值對時:

      作為值:

      const obj = {
          undefined: undefined,fn() {},symbol: Symbol()
      };
      
      JSON.stringify(obj); // {}
      

      作為鍵:

      const fn = function () {};
      const obj = {
          [undefined]: undefined,[fn]: function () {},[Symbol()]: Symbol()
      };
      
      JHosJaSON.stringify(obj); // {}
      

      undefined、function和symbol作為物件的key和value時,會在序列化時將其忽略

      ⚠️:此時可能會改變物件原有的順序,因為上述三種資料會在序列化時被忽略

      作為陣列值時:

      const arr = [undefined,function fn() {},Symbol()];
      
      JSON.stringify(arr); // [null,null,null]
      

      undefined、function和symbol作為陣列的value時,會在序列化時將其都轉換為null

      單獨存在時:

      JSON.stringify(undefined); // undefined
      JSON.stringify(function () {}); // undefined
      JSON.stringify(Symbol()); // undefined
      

      undefined、function和symbol單獨存在時,會在序列化時都轉換為undefined

      特性五

      序列化過程中,僅會序列化可列舉屬性,不可列舉屬性將會忽視

      const obj = {
          name: "nordon",age: 18,};
      
      // 將awww.cppcns.comge修改為不可列舉屬性
      Object.defineProperty(obj,"age",{
          enumerable: false,});
      
      JSON.stringify(obj); // {"name":"nordon"}
      

      ⚠️:此舉也會改變物件的原有順序

      特性六

      迴圈引用的物件,會在序列化時丟擲異常

      const obj = {
          name: "nordon",}www.cppcns.com;
      
      const p = {
          name: 'wy',obj
      }
      
      obj.p = p
      
      JSON.stringify(obj);
      

      此時會導致控制檯丟擲異常:

      Uncaught TypeError: Converting circular structure to JSON --> starting at object with constructor 'Object' | property 'p' -> object with constructor 'Object' --- property 'obj' closes the circle at JSON.stringify (<anonymous>)

      手動實現stringify

      明白了JSON.stringify的一些特性,接下來便可以依據這些特性動手實現一個kack版本

      在動手實現之前,先利用柯里化封裝一些資料型別校驗的工具函式:

      const currying = (fn,...outParams) => {
          // 獲取 fn 函式需要的引數個數
          const paramsLen = fn.length;
      
          return (...args) => {
              // 收集全部引數
              let params = [...outParams,...args];
              // 若引數沒有達到 fn 需要的引數,繼續收集引數
              if (params.length < paramsLen) {
                  return currying(fn,...params);
              }
      
              return fn(...params);
          };
      };
      
      /**
       * type: 型別 - [object Array]、[object Number]等
       * source: 資料來源
       */
      const judgeType = (type,source) => {
          return Object.prototype.toString.call(source) === type;
      };
      
      const isUndefined = currying(judgeType,"[object Undefined]");
      const isSymbol = currying(judgeType,"[object Symbol]");
      const isFunction = currying(judgeType,"[object Function]");
      const isObject = currying(judgeType,"[object Object]");
      const isNull = currying(judgeType,"[object Null]");
      

      下面直接上程式碼:

      function jsonStringify(data) {
          let type = typeof data;
      
          if (isNull(data)) { 
      // null 直接返回 字串'null'
              return "null";
          } else if (data.toJSON && typeof data.toJSON === "function") {
      // 配置了 toJSON函式, 直接使用 toJSON 返回的資料且忽略其他資料
              return jsonStringify(data.toJSON());
          } else if (Array.isArray(data)) {
              let result = [];
              //如果是陣列,那麼數組裡面的每一項型別又有可能是多樣的
              data.forEach((item,index) => {
                  if (isUndefined(item) || isSymbol(item) || isFunction(item)) {
                      result[index] = "null";
                  } else {
                      result[index] = jsonStringify(item);
                  }
              });
      
              result = "[" + result + "]";
      
              return result.replace(/'/g,'"');
          } else if (isObject(data)) {
              // 處理普通物件
              let result = [];
              Object.keys(data).forEach((item,index) => {
                  if (typeof item !== "symbol") {
                      //key 如果是 symbol 物件,忽略
                      if (
                          data[item] !== undefined &&
                          typeof data[item] !== "function" &&
                          typeof data[item] !== "symbol"
                      ) {
               客棧           //鍵值如果是 undefined、function、symbol 為屬性值,忽略
                          result.push(
                              '"' + item + '"' + ":" + jsonStringify(data[item])
                          );
                      }
                  }
              });
      
              return ("{" + result + "}").replace(/'/g,'"');
          } else if (type !== "object") {
              let result = data;
      
              //data 可能是基礎資料型別的情況在這裡處理
              if (Number.isNaN(data) || data === Infinity) {
                  //NaN 和 Infinity 序列化返回 "null"
                  result = "null";
              } else if (isUndefined(data) || isSymbol(data) || isFunction(data)) {
                  // 由於 function 序列化返回 undefined,因此和 undefined、symbol 一起處理
                  return undefined;
              } else if (type === "string") {
                  result = '"' + data + '"';
              }
      
              return String(result);
          }
      }
      

      至此簡易版本的JSON.stringify完成,雖然能力尚欠缺許多,主要是提供一個思路,核心註釋已註釋在程式碼中,可結合程式碼和上文的特性一起理解

      總結

      到此這篇關於JSON.stringify實現及其六大特性詳解的文章就介紹到這了,更多相關簡易版本JSON.stringify及特性內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!