JSON.stringify を理解し、その使用方法を確認します。

青灯夜游
リリース: 2022-03-10 19:37:25
転載
2308 人が閲覧しました

強力な JSON.stringify メソッドの使用方法を本当に知っていますか?以下の記事でJSON.stringifyの詳しい理解と使い方を紹介していますので、お役に立てれば幸いです。

JSON.stringify を理解し、その使用方法を確認します。

JSON.stringify 日々の開発でよく使われる手法ですが、本当に柔軟に使えるのでしょうか?

この記事を学ぶ前に、Xiaobao は皆さんにいくつかの質問を受けて、stringify について詳しく一緒に学んでほしいと考えています。 [関連する推奨事項: javascript ビデオ チュートリアル ]

  • stringify 関数には複数のパラメータがあります。各パラメータの用途は何ですか?
  • stringify シリアル化のガイドラインとは何ですか?
    • 関数のシリアル化をどのように扱うか?
    • null、未定義、NaN およびその他の特殊な値
    • ES6 後で追加された Symbol 型と BigInt はシリアル化プロセス中に特別に扱われますか?
  • stringify なぜディープ コピーに適していないのですか?
  • stringify?
の素晴らしい使用方法を思いつきますか?

記事全体の文脈は以下のマインド マップと一致しています。最初に印象を残してください。

JSON.stringify を理解し、その使用方法を確認します。

3 つのパラメータ

日常のプログラミングでは、JSON.stringify メソッドを使用して、 object JSON 文字列形式に変換します。

const stu = {
    name: 'zcxiaobao',
    age: 18
}

// {"name":"zcxiaobao","age":18}
console.log(JSON.stringify(stu));
ログイン後にコピー

でも stringify 本当にそんなに簡単なのでしょうか?まず、MDNstringify の定義を見てみましょう。

MDN の内容: JSON.stringify() メソッドは、JavaScript オブジェクトまたは値を JSON 文字に変換します。 replacer 関数が指定されている場合は文字列で、オプションで値を置換します。または、指定された replacer が配列の場合は、オプションで配列で指定されたプロパティのみを含めます。

定義を読んだ後、Xiaabao は驚きました。stringfy パラメータは複数あるのですか?もちろん、stringify には 3 つのパラメータがあります。

stringify 構文とパラメーターの概要を見てみましょう:

JSON.stringify(value[, replacer [, space]])
ログイン後にコピー
  • value: JSON 文字列にシーケンス化される値。
  • replacer(オプション)
    • パラメータが function の場合、シリアル化プロセス中に、値のシリアル化された Each 属性がこの関数によって変換および処理されます。

    • パラメータが array の場合、この配列に含まれる属性名のみが最終的な配列にシリアル化されます。 JSON string

    • このパラメータが null であるか指定されていない場合、オブジェクトのすべてのプロパティはシリアル化されます。

  • space(オプション): インデントに使用される空白文字列を指定します。出力を美しくするために使用されます。パラメータはスペースの数を表す数値です。上限は 10 です。
    • 値が 1 未満の場合は、スペースがないことを意味します。
    • パラメータが文字列の場合 (文字列の長さが を超える場合) 10 文字 (最初の 10 文字)、文字列はスペースとして扱われます。
    • # このパラメータが指定されていない場合 (または null の場合)、スペースはありません

    replacer

replacer を使用してみましょう。

replacer
    関数として
  • replacer
  • 関数として、これには 2 つのパラメーターがあります。キー (
key

) と値 (value)、および両方のパラメーターはシリアル化されます。 最初に、replacer 関数は、文字列化されるオブジェクト を表す空の文字列でキー値として渡されます。これを理解することが重要です。

replacer

関数は、オブジェクトの起動時にオブジェクトをキーと値のペアに解析せず、シリアル化する オブジェクトを最初に渡します。次に、各オブジェクトまたは配列のプロパティが順番に渡されます。 関数の戻り値が未定義または関数の場合、属性値はフィルターで除外されます、残りは戻り規則に従います。

// repalcer 接受两个参数 key value
// key value 分别为对象的每个键值对
// 因此我们可以根据键或者值的类型进行简单筛选
function replacer(key, value) {
  if (typeof value === "string") {
    return undefined;
  }
  return value;
}
// function 可自己测试
function replacerFunc(key, value) {
  if (typeof value === "string") {
    return () => {};
  }
  return value;
}
const foo = {foundation: "Mozilla", model: "box", week: 45, transport: "car", month: 7};
const jsonString = JSON.stringify(foo, replacer);
ログイン後にコピー
JSON シリアル化の結果は {"week":45,"month":7}

ただし、シリアル化が配列の場合、replacer 関数が unknown

または関数を返した場合、現在の値は無視されませんが、

null に置き換えられます。

const list = [1, '22', 3]
const jsonString = JSON.stringify(list, replacer)
ログイン後にコピー
JSON シリアル化された結果は '[1,null,3]'です

#replacer 配列として

  • 配列として表すと分かりやすく、配列内に出現するキー値をフィルタリングします。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">const foo = {foundation: &quot;Mozilla&quot;, model: &quot;box&quot;, week: 45, transport: &quot;car&quot;, month: 7}; const jsonString = JSON.stringify(foo, [&amp;#39;week&amp;#39;, &amp;#39;month&amp;#39;]);</pre><div class="contentsignin">ログイン後にコピー</div></div><p>JSON 序列化结果为 <code>{"week":45,"month":7}, 只保留 weekmonth 属性值。

    九特性

    特性一: undefined、函数、Symbol值

    • 出现在非数组对象属性值中: undefined、任意函数、Symbol 值在序列化过程中将会被忽略

    • 出现在数组中: undefined、任意函数、Symbol值会被转化为 null

    • 单独转换时: 会返回 undefined

    // 1. 对象属性值中存在这三种值会被忽略
    const obj = {
      name: &#39;zc&#39;,
      age: 18,
      // 函数会被忽略
      sayHello() {
        console.log(&#39;hello world&#39;)
      },
      // undefined会被忽略
      wife: undefined,
      // Symbol值会被忽略
      id: Symbol(111),
      // [Symbol(&#39;zc&#39;)]: &#39;zc&#39;,
    }
    // 输出结果: {"name":"zc","age":18}
    console.log(JSON.stringify(obj));
    
    // 2. 数组中这三种值会被转化为 null
    const list = [
      &#39;zc&#39;, 
      18, 
      // 函数转化为 null
      function sayHello() {
        console.log(&#39;hello world&#39;)
      }, 
      // undefined 转换为 null
      undefined, 
      // Symbol 转换为 null
      Symbol(111)
    ]
    
    // ["zc",18,null,null,null]
    console.log(JSON.stringify(list))
    
    // 3. 这三种值单独转化将会返回 undefined
    
    console.log(JSON.stringify(undefined))  // undefined
    console.log(JSON.stringify(Symbol(111))) // undefined
    console.log(JSON.stringify(function sayHello() { 
      console.log(&#39;hello world&#39;)
    })) // undefined
    ログイン後にコピー

    特性二: toJSON() 方法

    转换值如果有 toJSON() 方法,toJSON() 方法返回什么值,序列化结果就返回什么值,其余值会被忽略。

    const obj = {
      name: &#39;zc&#39;,
      toJSON(){
        return &#39;return toJSON&#39;
      }
    }
    // return toJSON
    console.log(JSON.stringify(obj));
    ログイン後にコピー

    特性三: 布尔值、数字、字符串的包装对象

    布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值

    JSON.stringify([new Number(1), new String("zcxiaobao"), new Boolean(true)]);
    // [1,"zcxiaobao",true]
    ログイン後にコピー

    特性四: NaN Infinity null

    特性四主要针对 JavaScript 里面的特殊值,例如 Number 类型里的 NaNInfinity 及 null 。此三种数值序列化过程中都会被当做 null

    // [null,null,null,null,null]
    JSON.stringify([null, NaN, -NaN, Infinity, -Infinity])
    
    // 特性三讲过布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值
    // 隐式类型转换就会调用包装类,因此会先调用 Number => NaN
    // 之后再转化为 null
    // 1/0 => Infinity => null
    JSON.stringify([Number(&#39;123a&#39;), +&#39;123a&#39;, 1/0])
    ログイン後にコピー

    特性五: Date对象

    Date 对象上部署了 toJSON 方法(同 Date.toISOString())将其转换为字符串,因此 JSON.stringify() 将会序列化 Date 的值为时间格式字符串

    // "2022-03-06T08:24:56.138Z"
    JSON.stringify(new Date())
    ログイン後にコピー

    特性六: Symbol

    特性一提到,Symbol 类型当作值来使用时,对象、数组、单独使用分别会被忽略、转换为 null 、转化为 undefined

    同样的,所有以 Symbol 为属性键的属性都会被完全忽略掉,即便 replacer 参数中强制指定包含了它们

    const obj = {
      name: &#39;zcxiaobao&#39;,
      age: 18,
      [Symbol(&#39;lyl&#39;)]: &#39;unique&#39;
    }
    function replacer(key, value) {
      if (typeof key === &#39;symbol&#39;) {
        return value;
      }
    }
    
    // undefined
    JSON.stringify(obj, replacer);
    ログイン後にコピー

    通过上面案例,我们可以看出,虽然我们通过 replacer 强行指定了返回 Symbol 类型值,但最终还是会被忽略掉。

    特性七: BigInt

    JSON.stringify 规定: 尝试去转换 BigInt 类型的值会抛出 TypeError

    const bigNumber = BigInt(1)
    // Uncaught TypeError: Do not know how to serialize a BigInt
    console.log(JSON.stringify(bigNumber))
    ログイン後にコピー

    特性八: 循环引用

    特性八指出: 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误

    日常开发中深拷贝最简单暴力的方式就是使用 JSON.parse(JSON.stringify(obj)),但此方法下的深拷贝存在巨坑,关键问题就在于 stringify 无法处理循环引用问题。

    const obj = {
      name: &#39;zcxiaobao&#39;,
      age: 18,
    }
    
    const loopObj = {
      obj
    }
    // 形成循环引用
    obj.loopObj = loopObj;
    JSON.stringify(obj)
    
    /* Uncaught TypeError: Converting circular structure to JSON
        --> starting at object with constructor &#39;Object&#39;
        |     property &#39;loopObj&#39; -> object with constructor &#39;Object&#39;
        --- property &#39;obj&#39; closes the circle
        at JSON.stringify (<anonymous>)
        at <anonymous>:10:6
    */
    ログイン後にコピー

    特性九: 可枚举属性

    对于对象(包括 Map/Set/WeakMap/WeakSet)的序列化,除了上文讲到的一些情况,stringify 也明确规定,仅会序列化可枚举的属性

    // 不可枚举的属性默认会被忽略
    // {"age":18}
    JSON.stringify(
        Object.create(
            null,
            {
                name: { value: &#39;zcxiaobao&#39;, enumerable: false },
                age: { value: 18, enumerable: true }
            }
        )
    );
    ログイン後にコピー

    六妙用

    localStorage

    localStorage 对象用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去删除。通常我们以对象形式进行存储。

    • 单纯调用 localStorage 对象方法

    const obj = {
      name: &#39;zcxiaobao&#39;,
      age: 18
    }
    // 单纯调用 localStorage.setItem()
    
    localStorage.setItem(&#39;zc&#39;, obj);
    
    // 最终返回结果是 [object Object]
    // 可见单纯调用localStorage是失败的
    console.log(localStorage.getItem(&#39;zc&#39;))
    ログイン後にコピー
    • localStorage 配合 JSON.stringify 方法

    localStorage.setItem(&#39;zc&#39;, JSON.stringify(obj));
    
    // 最终返回结果是 {name: &#39;zcxiaobao&#39;, age: 18}
    console.log(JSON.parse(localStorage.getItem(&#39;zc&#39;)))
    ログイン後にコピー

    属性过滤

    来假设这样一个场景,后端返回了一个很长的对象,对象里面属性很多,而我们只需要其中几个属性,并且这几个属性我们要存储到 localStorage 中。

    • 方案一: 解构赋值+ stringify

    // 我们只需要 a,e,f 属性
    const obj = {
      a:1, b:2, c:3, d:4, e:5, f:6, g:7
    }
    // 解构赋值
    const {a,e,f} = obj;
    // 存储到localStorage
    localStorage.setItem(&#39;zc&#39;, JSON.stringify({a,e,f}))
    // {"a":1,"e":5,"f":6}
    console.log(localStorage.getItem(&#39;zc&#39;))
    ログイン後にコピー
    • 使用 stringifyreplacer 参数

    // 借助 replacer 作为数组形式进行过滤
    localStorage.setItem(&#39;zc&#39;, JSON.stringify(obj, [&#39;a&#39;,&#39;e&#39;,&#39;f&#39;]))
    // {"a":1,"e":5,"f":6}
    console.log(localStorage.getItem(&#39;zc&#39;))
    ログイン後にコピー

    replacer 是数组时,可以简单的过滤出我们所需的属性,是一个不错的小技巧。

    三思而后行之深拷贝

    使用 JSON.parse(JSON.stringify) 是实现对象的深拷贝最简单暴力的方法之一。但也正如标题所言,使用该种方法的深拷贝要深思熟虑。

    • 循环引用问题,stringify 会报错

    • 函数、undefinedSymbol 会被忽略

    • NaNInfinity-Infinity 会被序列化成 null

    • ...

    因此在使用 JSON.parse(JSON.stringify) 做深拷贝时,一定要深思熟虑。如果没有上述隐患,JSON.parse(JSON.stringify) 是一个可行的深拷贝方案。

    对象的 map 函数

    在使用数组进行编程时,我们会经常使用到 map 函数。有了 replacer 参数后,我们就可以借助此参数,实现对象的 map 函数。

    const ObjectMap = (obj, fn) => {
      if (typeof fn !== "function") {
        throw new TypeError(`${fn} is not a function !`);
      }
      // 先调用 JSON.stringify(obj, replacer) 实现 map 功能
      // 然后调用 JSON.parse 重新转化成对象
      return JSON.parse(JSON.stringify(obj, fn));
    };
    
    // 例如下面给 obj 对象的属性值乘以2
    
    const obj = {
      a: 1,
      b: 2,
      c: 3
    }
    console.log(ObjectMap(obj, (key, val) => {
      if (typeof value === "number") {
        return value * 2;
      }
      return value;
    }))
    ログイン後にコピー

    很多同学有可能会很奇怪,为什么里面还需要多加一部判断,直接 return value * 2 不可吗?

    上文讲过,replacer 函数首先传入的是待序列化对象,对象 * 2 => NaN => toJSON(NaN) => undefined => 被忽略,就没有后续的键值对解析了。

    删除对象属性

    借助 replacer 函数,我们还可以删除对象的某些属性。

    const obj = {
      name: &#39;zcxiaobao&#39;,
      age: 18
    }
    // {"age":18}
    JSON.stringify(obj, (key, val) => {
      // 返回值为 undefined时,该属性会被忽略 
      if (key === &#39;name&#39;) {
        return undefined;
      }
      return val;
    })
    ログイン後にコピー

    对象判断

    JSON.stringify 可以将对象序列化为字符串,因此我们可以借助字符串的方法来实现简单的对象相等判断。

    //判断数组是否包含某对象
    const names = [
      {name:&#39;zcxiaobao&#39;},
      {name:&#39;txtx&#39;},
      {name:&#39;mymy&#39;},
    ];
    const zcxiaobao = {name:&#39;zcxiaobao&#39;};
    // true
    JSON.stringify(names).includes(JSON.stringify(zcxiaobao))
     
    // 判断对象是否相等
    const d1 = {type: &#39;div&#39;}
    const d2 = {type: &#39;div&#39;}
    
    // true
    JSON.stringify(d1) === JSON.stringify(d2);
    ログイン後にコピー

    数组对象去重

    借助上面的思想,我们还能实现简单的数组对象去重。

    但由于 JSON.stringify 序列化 {x:1, y:1}{y:1, x:1} 结果不同,因此在开始之前我们需要处理一下数组中的对象。

    • 方法一: 将数组中的每个对象的键按字典序排列

    arr.forEach(item => {
      const newItem = {};
      Object.keys(item)   // 获取对象键值
            .sort()       // 键值排序
            .map(key => { // 生成新对象
              newItem[key] = item[key];
            })
      // 使用 newItem 进行去重操作
    })
    ログイン後にコピー

    但方法一有些繁琐,JSON.stringify 提供了 replacer 数组格式参数,可以过滤数组。

    • 方法二: 借助 replacer 数组格式

    function unique(arr) {
      const keySet = new Set();
      const uniqueObj = {}
      // 提取所有的键
      arr.forEach(item => {
        Object.keys(item).forEach(key => keySet.add(key))
      })
      const replacer = [...keySet];
      arr.forEach(item => {
        // 所有的对象按照规定键值 replacer 过滤
        unique[JSON.stringify(item, replacer)] = item;
      })
      return Object.keys(unique).map(u => JSON.parse(u))
    }
    
    // 测试一下
    unique([{}, {}, 
          {x:1},
          {x:1},
          {a:1},
          {x:1,a:1},
          {x:1,a:1},
          {x:1,a:1,b:1}
          ])
    
    // 返回结果
    [{},{"x":1},{"a":1},{"x":1,"a":1},{"x":1,"a":1,"b":1}]
    ログイン後にコピー

    【相关推荐:web前端

    以上がJSON.stringify を理解し、その使用方法を確認します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:juejin.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート