
JSON.stringifyは日常の開発で頻繁に使用されるメソッドで、本当に柔軟に使用できますか?
Xiaobao では、この記事を学ぶ前に、皆さんにいくつかの質問を受けてstringify詳しく学んでもらいたいと考えています。
stringify関数にはいくつかのパラメータがあります。各パラメータの用途は何ですか?stringifynull、undefined、NaNなどの特殊な値はどのように処理されますか?ES6 Symbol型とBigIntのシリアル化プロセス中に特別な処理が行われますか?stringifystringifyコピーに適していないのはなぜ記事全体のコンテキストは以下のマインド マップと一致しています。最初に印象を残すことができます。

日常のプログラミングでは、 JSON.stringifyメソッドを使用してオブジェクトをJSON文字列形式に変換することがよくあります。
const stu = {
名前: 'zcxiaobao'、
年齢: 18
}
// {"名前":"zcxiaabao","年齢":18}
console.log(JSON.stringify(stu));しかし、 stringify本当に簡単なのでしょうか?まずMDNのstringifyの定義を見てみましょう。
MDN によると、 JSON.stringify()メソッドはJavaScriptオブジェクトまたは値をJSON文字列に変換します。 replacer関数が指定されている場合、値はオプションで置換できます。または、指定された置換関数には配列で指定されたreplacerが含まれます。 。
定義を読んだ後、Xiaabao はstringfy複数のパラメータがあるのかと驚きました。もちろん、 stringify 3 つのパラメータがあります。
stringify構文とパラメーターの概要を見てみましょう。
JSON.stringify(value[, replacer [, space]])
value : JSON 文字列にシーケンス化される値。replacer (オプション)パラメータがfunctionの場合、シリアル化プロセス中に、シリアル化された値の各属性が関数によって変換および処理されます。
パラメータがarrayの場合、この配列に含まれるプロパティのみが属性のみになります。
このパラメータがnullあるか指定されていない場合、オブジェクトのすべての属性がシリアル化されます
JSON
space (オプション): インデントに使用される空白文字列を指定します。パラメータが数値の場合は、スペースの数を表します。上限は 10 です。
値が 1 未満の場合、
パラメータが文字列の場合 (文字列の長さが 10 文字を超える場合は、最初の 10 文字が取得されます)、文字列はスペースとして扱われます
。パラメータが指定されていない(または null である)場合、スペースはありません
replacer使用してみましょう。
replacer関数としての
replacer関数としての replacer には、キー ( key ) と値 ( value ) の 2 つのパラメーターがあり、両方のパラメーターはシリアル化されます。
最初に、 replacer 関数には、文字列化されるオブジェクトを表す空の文字列がキー値として渡されます。これを理解することが重要です。 replacer関数は、オブジェクトが起動されたときに、オブジェクトをキーと値のペアに解析するのではなく、シリアル化するオブジェクトを最初に渡します。次に、各オブジェクトまたは配列のプロパティが順番に渡されます。関数の戻り値が未定義または関数の場合、属性値はフィルターで除外され、残りは戻り規則に従います。
// repalcer は 2 つのパラメータのキー値を受け入れます
// キー値はオブジェクトの各キーと値のペアです // したがって、キーまたは値のタイプに基づいて単純にフィルタリングできます function replacer(key, value) {
if (値の型 === "文字列") {
未定義を返します。
}
戻り値;
}
// 関数は関数自体をテストできます replacerFunc(key, value) {
if (値の型 === "文字列") {
return () => {};
}
戻り値;
}
const foo = {財団: "Mozilla"、モデル: "ボックス"、週: 45、輸送: "車"、月: 7};
const jsonString = JSON.stringify(foo, replacer); JSONシリアル化の結果は{"week":45,"month":7}
ですが、シリアル化が配列の場合、 replacer関数がundefinedまたは function を返す場合は、現在の値が返されます。無視されず、 nullに置き換えられます。
const リスト = [1, '22', 3] const jsonString = JSON.stringify(list, replacer)
JSONシリアル化の結果は '[1,null,3]' になります。
replacer
配列として理解しやすく、配列内に出現するキー値をフィルタリングします。
const foo = {財団: "Mozilla"、モデル: "ボックス"、週: 45、輸送: "車"、月: 7};
const jsonString = JSON.stringify(foo, ['week', 'month']); JSON シリアル化の結果は{"week":45,"month":7}となり、 weekとmonth属性値のみが返されます。保持されます。
配列内にundefined
する: undefined 、任意の関数、およびSymbol値はシリアル化プロセス中に無視されます。
およびSymbol値は無視されます。null
のみに変換された場合: unknownが返されます。
// 1. オブジェクト属性値内のこれら 3 つの値の存在は無視されます。 const obj = {
名前: 'zc'、
年齢:18歳、
// 関数は無視されます SayHello() {
console.log('ハローワールド')
}、
// 未定義は無視されます 妻: 未定義,
// シンボル値は無視されます id: Symbol(111),
// [シンボル('zc')]: 'zc',
}
// 出力結果: {"name":"zc","age":18}
console.log(JSON.stringify(obj));
// 2. 配列内のこれら 3 つの値は null に変換されます
定数リスト = [
「zc」、
18、
// 関数が null に変換される
関数sayHello() {
console.log('ハローワールド')
}、
// 未定義は null に変換されます
未定義、
// シンボルが null に変換される
記号(111)
】
// ["zc",18,null,null,null]
console.log(JSON.stringify(リスト))
// 3. これら 3 つの値を個別に変換すると、unknown が返されます
console.log(JSON.stringify(unknown)) // 未定義
console.log(JSON.stringify(Symbol(111))) // 未定義
console.log(JSON.stringify(関数 SayHello() {
console.log('ハローワールド')
})) // 未定義の値を変換します。 toJSON()メソッドがある場合、 toJSON()メソッドが返す値はシリアル化結果で返される値となり、その他の値は次のようになります。無視されました。
const obj = {
名前: 'zc'、
toJSON(){
return 'JSON に戻る'
}
}
//JSON に戻ります
console.log(JSON.stringify(obj));ブール値、数値、文字列のパッケージ化オブジェクトは、シリアル化プロセス中に対応する元の値の JSON に自動的に変換されます。
. stringify([new Number(1), new String("zcxiaabao"), new Boolean(true)]);
// [1,"zcxiaabao",true]機能 4 は主に、 Number型のNaN 、 Infinity 、 null など、 JavaScriptの特殊な値を対象としています。これら 3 種類の値は、シリアル化中にnullとして扱われます。
// [null,null,null,null,null]
JSON.stringify([null, NaN, -NaN, Infinity, -Infinity])
// 機能 3 では、ブール値、数値、文字列のパッケージング オブジェクトがシリアル化プロセス中に対応する元の値に自動的に変換されると述べました // 暗黙的な型変換によりパッケージング クラスが呼び出されるため、Number => NaN になります。最初に呼ばれた
// 次に null に変換します
// 1/0 => 無限 => null
JSON.stringify([Number('123a'), +'123a', 1/0])toJSONメソッド ( Date.toISOString()と同じ) がDateオブジェクトをstring なので、 JSON.stringify() は Date 値を時刻形式の string にシリアル化します。
// "2022-03-06T08:24:56.138Z" JSON.stringify(new Date())
シンボル機能について言及する場合、 Symbolタイプが値として使用される場合、オブジェクト、配列、および個々の使用はそれぞれ無視され、 nullに変換され、 undefinedに変換されます。
同様に、プロパティ キーとして Symbol を持つすべてのプロパティは、たとえ強制的に replacer パラメータに含めたとしても、完全に無視されます。
const obj = {
名前: 'zcxiaobao'、
年齢:18歳、
[シンボル('lyl')]: 'ユニーク'
}
関数 replacer(キー, 値) {
if (キーの種類 === 'シンボル') {
戻り値;
}
}
// 未定義
JSON.stringify(obj, replacer);上記の例から、 replacerを通じて戻り値のSymbol型の値を強制的に指定しても、最終的には無視されることがわかります。
JSON.stringify次のように規定しています: BigInt型の値を変換しようとするとTypeErrorがスローされます
const bigNumber = BigInt(1) // キャッチされない TypeError: BigInt をシリアル化する方法がわかりません Console.log(JSON.stringify(bigNumber))
機能 8 は、循環参照を含むオブジェクト (オブジェクトが相互に参照し、無限ループを形成する) に対してこのメソッドを実行すると、エラーをスローします
。最も単純かつ最も暴力的な方法はJSON.parse(JSON.stringify(obj))を使用することですが、この方法のディープ コピーには大きな落とし穴があります。それは、 stringify循環参照の問題を処理できないことです。
const obj = {
名前: 'zcxiaobao'、
年齢:18歳、
}
const ループオブジェクト = {
オブジェクト
}
// 循環参照を形成します obj.loopObj = loopObj;
JSON.stringify(obj)
/* Uncaught TypeError: 循環構造を JSON に変換しています
--> コンストラクター 'Object' を使用してオブジェクトから開始
| プロパティ 'loopObj' -> コンストラクター 'Object' を持つオブジェクト
--- プロパティ 'obj' が円を閉じます
JSON.stringify (<匿名>) で
<匿名> で:10:6
*/オブジェクトの列挙可能なプロパティ ( Map/Set/WeakMap/WeakSetを含む) のシリアル化の場合、上記のいくつかの状況に加えて、 stringify列挙可能なプロパティのみがシリアル化されることも明確に規定しています。
// 列挙不可プロパティはデフォルトでは無視されます // {"age":18}
JSON.stringify(
オブジェクト.create(
ヌル、
{
名前: { 値: 'zcxiaabao'、列挙可能: false }、
年齢: { 値: 18、列挙可能: true }
}
)
); localStorageオブジェクトは、Web サイト全体のデータを長期間保存するために使用されます。保存されたデータには、手動で削除するまで有効期限がありません。通常、私たちはそれをオブジェクトの形で保存します。
localStorageオブジェクトのメソッド
const obj = {を呼び出すだけです。
名前: 'zcxiaobao'、
年齢: 18
}
// localStorage.setItem() を呼び出すだけです
localStorage.setItem('zc', obj);
//最終的な戻り結果は [object Object] です
// localStorage を呼び出すだけでは失敗することがわかります console.log(localStorage.getItem('zc')) localStorage JSON.stringifyメソッドと連携します
localStorage.setItem('zc', JSON.stringify(obj));
//最終的な戻り結果は {name: 'zcxiaabao'、年齢: 18} です
Console.log(JSON.parse(localStorage.getItem('zc')))、そのようなシナリオを想定しており、バックエンドは多くの属性を含む長いオブジェクトを返し、そのうちのいくつかのみを保存する必要があります。 localStorageの属性。
オプション 1: 代入の構造化 + 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('zc', JSON.stringify({a,e,f}))
// {"a":1,"e":5,"f":6}
console.log(localStorage.getItem('zc')) はstringifyのreplacerパラメータを使用します
// replacer を使用して配列としてフィルタリングします localStorage.setItem('zc', JSON.stringify(obj, ['a','e' 、「f」]))
// {"a":1,"e":5,"f":6}
console.log(localStorage.getItem('zc')) replacer配列の場合、必要な属性を単純に除外できます。これは良いちょっとしたトリックです。
JSON.parse(JSON.stringify)の使用は、オブジェクトのディープ コピーを実装する最も単純かつ最も暴力的な方法の 1 つです。ただし、タイトルにあるように、このディープ コピー方法を使用するには慎重な検討が必要です。
循環参照の問題、 stringifyエラー関数を報告する
、 undefined 、 Symbolが無視される、
NaN 、 Infinity 、 -Infinityがnullにシリアル化される
...
したがって、 JSON.parse(JSON.stringify)使用してディープ コピーを行う場合は、次のようにする必要があります。慎重に考えてください。上記の隠れた危険がない場合、 JSON.parse(JSON.stringify)は実行可能なディープ コピー ソリューションです。
配列を使ってプログラミングする場合、よくmap関数を使います。 replacerパラメーターを使用すると、このパラメーターを使用してオブジェクトのmap関数を実装できます。
const ObjectMap = (obj, fn) => {
if (typeof fn !== "関数") {
throw new TypeError(`${fn} は関数ではありません !`);
}
// まず JSON.stringify(obj, replacer) を呼び出してマップ関数を実装します // 次に JSON.parse を呼び出してオブジェクトに再変換します return JSON.parse(JSON.stringify(obj, fn));
};
// たとえば、次の例では、obj オブジェクトの属性値を 2 倍します。
const obj = {
答え: 1、
b:2、
c:3
}
console.log(ObjectMap(obj, (key, val) => {
if (値の種類 === "数値") {
戻り値 * 2;
}
戻り値;
}))多くの学生は、なぜ追加の判定が必要なのか疑問に思っているかもしれませんreturn value * 2だけではだめなのでしょうか。
前述したように、 replacer関数は最初にシリアル化するオブジェクトを渡します。 object * 2 => NaN => toJSON(NaN) => unknown => は無視され、その後のキーと値のペアの分析は行われません。
replacer関数を使用すると、オブジェクトの特定の属性を削除することもできます。
const obj = {
名前: 'zcxiaobao'、
年齢: 18
}
// {"年齢":18}
JSON.stringify(obj, (key, val) => {
// 戻り値が未定義の場合、このプロパティは無視されます if (key === 'name') {
未定義を返します。
}
戻り値;
})JSON.stringifyオブジェクトを文字列にシリアル化できるため、文字列メソッドを使用して単純なオブジェクトの同等性判定を実装できます。
//配列にオブジェクトが含まれているかどうかを判断します const names = [
{名前:'zcxiaobao'}、
{name: 'txtx'}、
{名前:'マイミー'}、
];
const zcxiaabao = {名前:'zcxiaabao'};
// 真実
JSON.stringify(names).includes(JSON.stringify(zcxiaabao))
// オブジェクトが等しいかどうかを判断します const d1 = {type: 'div'}
const d2 = {type: 'div'}
// 真実
JSON.stringify(d1) === JSON.stringify(d2);上記のアイデアを利用して、単純な配列オブジェクトの重複排除も実現できます。
ただし、 JSON.stringifyシリアル化{x:1, y:1}と{y:1, x:1}の結果は異なるため、開始する前に配列内のオブジェクトを処理する必要があります。
方法 1: 配列内の各オブジェクトのキーを辞書順に並べます
arr.forEach(item => {
const newItem = {};
Object.keys(item) // オブジェクトのキーを取得 value.sort() // キーの値 sort.map(key => { // 新しいオブジェクトを生成 newItem[key] = item[key];
})
// newItemを使用して重複排除操作を実行します}) replacer 、 JSON.stringify 1つは少し面倒です。
方法 2: replacer配列形式
関数 unique(arr) {を使用する
const keySet = 新しい Set();
const siquimobj = {}
//すべてのキーを抽出します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))
}
//一意のテスト([{}, {},
{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}]