小編給大家分享一下javascript中JSON.stringify如何使用,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!
創(chuàng)新互聯(lián)建站專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于網(wǎng)站設(shè)計(jì)制作、網(wǎng)站建設(shè)、永川網(wǎng)絡(luò)推廣、成都小程序開發(fā)、永川網(wǎng)絡(luò)營銷、永川企業(yè)策劃、永川品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營等,從售前售中售后,我們都將竭誠為您服務(wù),您的肯定,是我們最大的嘉獎(jiǎng);創(chuàng)新互聯(lián)建站為所有大學(xué)生創(chuàng)業(yè)者提供永川建站搭建服務(wù),24小時(shí)服務(wù)熱線:18982081108,官方網(wǎng)址:www.cdcxhl.com
在日常編程中,我們經(jīng)常 JSON.stringify
方法將某個(gè)對象轉(zhuǎn)換成 JSON
字符串形式。
const stu = { name: 'zcxiaobao', age: 18 } // {"name":"zcxiaobao","age":18} console.log(JSON.stringify(stu));
但 stringify
真的就這么簡單嗎?我們先來看一下 MDN
中對 stringify
的定義。
MDN 中指出: JSON.stringify()
方法將一個(gè) JavaScript
對象或值轉(zhuǎn)換為 JSON
字符串,如果指定了一個(gè) replacer
函數(shù),則可以選擇性地替換值,或者指定的 replacer
是數(shù)組,則可選擇性地僅包含數(shù)組指定的屬性。
看完定義,小包就一驚,stringfy
不止一個(gè)參數(shù)嗎?當(dāng)然了,stringify
有三個(gè)參數(shù)。
咱們來看一下 stringify
語法和參數(shù)介紹:
JSON.stringify(value[, replacer [, space]])
value
: 將要序列后成 JSON 字符串的值。
replacer
(可選)
如果該參數(shù)是一個(gè)函數(shù),則在序列化過程中,被序列化的值的每個(gè)屬性都會(huì)經(jīng)過該函數(shù)的轉(zhuǎn)換和處理;
如果該參數(shù)是一個(gè)數(shù)組,則只有包含在這個(gè)數(shù)組中的屬性名才會(huì)被序列化到最終的 JSON
字符串中
如果該參數(shù)為 null
或者未提供,則對象所有的屬性都會(huì)被序列化。
space
(可選): 指定縮進(jìn)用的空白字符串,用于美化輸出
如果參數(shù)是個(gè)數(shù)字,它代表有多少的空格。上限為10。
該值若小于1,則意味著沒有空格
如果該參數(shù)為字符串(當(dāng)字符串長度超過10個(gè)字母,取其前10個(gè)字母),該字符串將被作為空格
如果該參數(shù)沒有提供(或者為 null),將沒有空格
我們來嘗試一下 replacer
的使用。
replacer
作為函數(shù)
replacer
作為函數(shù),它有兩個(gè)參數(shù),鍵(key
) 和 值(value
),并且兩個(gè)參數(shù)都會(huì)被序列化。
在開始時(shí),replacer 函數(shù)會(huì)被傳入一個(gè)空字符串作為 key 值,代表著要被 stringify 的這個(gè)對象。理解這點(diǎn)很重要,replacer
函數(shù)并非是上來就把對象解析成鍵值對形式,而是先傳入了待序列化對象。隨后每個(gè)對象或數(shù)組上的屬性會(huì)被依次傳入。 如果函數(shù)返回值為undefined或者函數(shù)時(shí),該屬性值會(huì)被過濾掉,其余按照返回規(guī)則。
// repalcer 接受兩個(gè)參數(shù) key value // key value 分別為對象的每個(gè)鍵值對 // 因此我們可以根據(jù)鍵或者值的類型進(jìn)行簡單篩選 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
序列化結(jié)果為 {"week":45,"month":7}
但如果序列化的是數(shù)組,若 replacer
函數(shù)返回 undefined
或者函數(shù),當(dāng)前值不會(huì)被忽略,而將會(huì)被 null
取代。
const list = [1, '22', 3] const jsonString = JSON.stringify(list, replacer)
JSON
序列化的結(jié)果為 '[1,null,3]'
replacer
作為數(shù)組
作為數(shù)組比較好理解,過濾數(shù)組中出現(xiàn)的鍵值。
const foo = {foundation: "Mozilla", model: "box", week: 45, transport: "car", month: 7}; const jsonString = JSON.stringify(foo, ['week', 'month']);
JSON 序列化結(jié)果為 {"week":45,"month":7}
, 只保留 week
和 month
屬性值。
出現(xiàn)在非數(shù)組對象屬性值中: undefined
、任意函數(shù)、Symbol
值在序列化過程中將會(huì)被忽略
出現(xiàn)在數(shù)組中: undefined
、任意函數(shù)、Symbol
值會(huì)被轉(zhuǎn)化為 null
單獨(dú)轉(zhuǎn)換時(shí): 會(huì)返回 undefined
// 1. 對象屬性值中存在這三種值會(huì)被忽略 const obj = { name: 'zc', age: 18, // 函數(shù)會(huì)被忽略 sayHello() { console.log('hello world') }, // undefined會(huì)被忽略 wife: undefined, // Symbol值會(huì)被忽略 id: Symbol(111), // [Symbol('zc')]: 'zc', } // 輸出結(jié)果: {"name":"zc","age":18} console.log(JSON.stringify(obj)); // 2. 數(shù)組中這三種值會(huì)被轉(zhuǎn)化為 null const list = [ 'zc', 18, // 函數(shù)轉(zhuǎn)化為 null function sayHello() { console.log('hello world') }, // undefined 轉(zhuǎn)換為 null undefined, // Symbol 轉(zhuǎn)換為 null Symbol(111) ] // ["zc",18,null,null,null] console.log(JSON.stringify(list)) // 3. 這三種值單獨(dú)轉(zhuǎn)化將會(huì)返回 undefined console.log(JSON.stringify(undefined)) // undefined console.log(JSON.stringify(Symbol(111))) // undefined console.log(JSON.stringify(function sayHello() { console.log('hello world') })) // undefined
轉(zhuǎn)換值如果有 toJSON()
方法,toJSON()
方法返回什么值,序列化結(jié)果就返回什么值,其余值會(huì)被忽略。
const obj = { name: 'zc', toJSON(){ return 'return toJSON' } } // return toJSON console.log(JSON.stringify(obj));
布爾值、數(shù)字、字符串的包裝對象在序列化過程中會(huì)自動(dòng)轉(zhuǎn)換成對應(yīng)的原始值
JSON.stringify([new Number(1), new String("zcxiaobao"), new Boolean(true)]); // [1,"zcxiaobao",true]
特性四主要針對 JavaScript
里面的特殊值,例如 Number
類型里的 NaN
和 Infinity
及 null 。此三種數(shù)值序列化過程中都會(huì)被當(dāng)做 null
。
// [null,null,null,null,null] JSON.stringify([null, NaN, -NaN, Infinity, -Infinity]) // 特性三講過布爾值、數(shù)字、字符串的包裝對象在序列化過程中會(huì)自動(dòng)轉(zhuǎn)換成對應(yīng)的原始值 // 隱式類型轉(zhuǎn)換就會(huì)調(diào)用包裝類,因此會(huì)先調(diào)用 Number => NaN // 之后再轉(zhuǎn)化為 null // 1/0 => Infinity => null JSON.stringify([Number('123a'), +'123a', 1/0])
Date
對象上部署了 toJSON
方法(同 Date.toISOString()
)將其轉(zhuǎn)換為字符串,因此 JSON.stringify() 將會(huì)序列化 Date 的值為時(shí)間格式字符串。
// "2022-03-06T08:24:56.138Z" JSON.stringify(new Date())
特性一提到,Symbol
類型當(dāng)作值來使用時(shí),對象、數(shù)組、單獨(dú)使用分別會(huì)被忽略、轉(zhuǎn)換為 null
、轉(zhuǎn)化為 undefined
。
同樣的,所有以 Symbol 為屬性鍵的屬性都會(huì)被完全忽略掉,即便 replacer 參數(shù)中強(qiáng)制指定包含了它們。
const obj = { name: 'zcxiaobao', age: 18, [Symbol('lyl')]: 'unique' } function replacer(key, value) { if (typeof key === 'symbol') { return value; } } // undefined JSON.stringify(obj, replacer);
通過上面案例,我們可以看出,雖然我們通過 replacer
強(qiáng)行指定了返回 Symbol
類型值,但最終還是會(huì)被忽略掉。
JSON.stringify
規(guī)定: 嘗試去轉(zhuǎn)換 BigInt
類型的值會(huì)拋出 TypeError
const bigNumber = BigInt(1) // Uncaught TypeError: Do not know how to serialize a BigInt console.log(JSON.stringify(bigNumber))
特性八指出: 對包含循環(huán)引用的對象(對象之間相互引用,形成無限循環(huán))執(zhí)行此方法,會(huì)拋出錯(cuò)誤
日常開發(fā)中深拷貝最簡單暴力的方式就是使用 JSON.parse(JSON.stringify(obj))
,但此方法下的深拷貝存在巨坑,關(guān)鍵問題就在于 stringify
無法處理循環(huán)引用問題。
const obj = { name: 'zcxiaobao', age: 18, } const loopObj = { obj } // 形成循環(huán)引用 obj.loopObj = loopObj; JSON.stringify(obj) /* Uncaught TypeError: Converting circular structure to JSON --> starting at object with constructor 'Object' | property 'loopObj' -> object with constructor 'Object' --- property 'obj' closes the circle at JSON.stringify () at :10:6 */
對于對象(包括 Map/Set/WeakMap/WeakSet
)的序列化,除了上文講到的一些情況,stringify
也明確規(guī)定,僅會(huì)序列化可枚舉的屬性
// 不可枚舉的屬性默認(rèn)會(huì)被忽略 // {"age":18} JSON.stringify( Object.create( null, { name: { value: 'zcxiaobao', enumerable: false }, age: { value: 18, enumerable: true } } ) );
localStorage
對象用于長久保存整個(gè)網(wǎng)站的數(shù)據(jù),保存的數(shù)據(jù)沒有過期時(shí)間,直到手動(dòng)去刪除。通常我們以對象形式進(jìn)行存儲(chǔ)。
單純調(diào)用 localStorage
對象方法
const obj = { name: 'zcxiaobao', age: 18 } // 單純調(diào)用 localStorage.setItem() localStorage.setItem('zc', obj); // 最終返回結(jié)果是 [object Object] // 可見單純調(diào)用localStorage是失敗的 console.log(localStorage.getItem('zc'))
localStorage
配合 JSON.stringify
方法
localStorage.setItem('zc', JSON.stringify(obj)); // 最終返回結(jié)果是 {name: 'zcxiaobao', age: 18} console.log(JSON.parse(localStorage.getItem('zc')))
來假設(shè)這樣一個(gè)場景,后端返回了一個(gè)很長的對象,對象里面屬性很多,而我們只需要其中幾個(gè)屬性,并且這幾個(gè)屬性我們要存儲(chǔ)到 localStorage
中。
方案一: 解構(gòu)賦值+ stringify
// 我們只需要 a,e,f 屬性 const obj = { a:1, b:2, c:3, d:4, e:5, f:6, g:7 } // 解構(gòu)賦值 const {a,e,f} = obj; // 存儲(chǔ)到localStorage localStorage.setItem('zc', JSON.stringify({a,e,f})) // {"a":1,"e":5,"f":6} console.log(localStorage.getItem('zc'))
使用 stringify
的 replacer
參數(shù)
// 借助 replacer 作為數(shù)組形式進(jìn)行過濾 localStorage.setItem('zc', JSON.stringify(obj, ['a','e','f'])) // {"a":1,"e":5,"f":6} console.log(localStorage.getItem('zc'))
當(dāng) replacer
是數(shù)組時(shí),可以簡單的過濾出我們所需的屬性,是一個(gè)不錯(cuò)的小技巧。
使用 JSON.parse(JSON.stringify)
是實(shí)現(xiàn)對象的深拷貝最簡單暴力的方法之一。但也正如標(biāo)題所言,使用該種方法的深拷貝要深思熟慮。
循環(huán)引用問題,stringify
會(huì)報(bào)錯(cuò)
函數(shù)、undefined
、Symbol
會(huì)被忽略
NaN
、Infinity
和 -Infinity
會(huì)被序列化成 null
...
因此在使用 JSON.parse(JSON.stringify)
做深拷貝時(shí),一定要深思熟慮。如果沒有上述隱患,JSON.parse(JSON.stringify)
是一個(gè)可行的深拷貝方案。
在使用數(shù)組進(jìn)行編程時(shí),我們會(huì)經(jīng)常使用到 map
函數(shù)。有了 replacer
參數(shù)后,我們就可以借助此參數(shù),實(shí)現(xiàn)對象的 map
函數(shù)。
const ObjectMap = (obj, fn) => { if (typeof fn !== "function") { throw new TypeError(`${fn} is not a function !`); } // 先調(diào)用 JSON.stringify(obj, replacer) 實(shí)現(xiàn) map 功能 // 然后調(diào)用 JSON.parse 重新轉(zhuǎn)化成對象 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; }))
很多同學(xué)有可能會(huì)很奇怪,為什么里面還需要多加一部判斷,直接 return value * 2
不可嗎?
上文講過,replacer
函數(shù)首先傳入的是待序列化對象,對象 * 2 => NaN => toJSON(NaN) => undefined => 被忽略,就沒有后續(xù)的鍵值對解析了。
借助 replacer
函數(shù),我們還可以刪除對象的某些屬性。
const obj = { name: 'zcxiaobao', age: 18 } // {"age":18} JSON.stringify(obj, (key, val) => { // 返回值為 undefined時(shí),該屬性會(huì)被忽略 if (key === 'name') { return undefined; } return val; })
JSON.stringify
可以將對象序列化為字符串,因此我們可以借助字符串的方法來實(shí)現(xiàn)簡單的對象相等判斷。
//判斷數(shù)組是否包含某對象 const names = [ {name:'zcxiaobao'}, {name:'txtx'}, {name:'mymy'}, ]; const zcxiaobao = {name:'zcxiaobao'}; // true JSON.stringify(names).includes(JSON.stringify(zcxiaobao)) // 判斷對象是否相等 const d1 = {type: 'div'} const d2 = {type: 'div'} // true JSON.stringify(d1) === JSON.stringify(d2);
借助上面的思想,我們還能實(shí)現(xiàn)簡單的數(shù)組對象去重。
但由于 JSON.stringify
序列化 {x:1, y:1}
和 {y:1, x:1}
結(jié)果不同,因此在開始之前我們需要處理一下數(shù)組中的對象。
方法一: 將數(shù)組中的每個(gè)對象的鍵按字典序排列
arr.forEach(item => { const newItem = {}; Object.keys(item) // 獲取對象鍵值 .sort() // 鍵值排序 .map(key => { // 生成新對象 newItem[key] = item[key]; }) // 使用 newItem 進(jìn)行去重操作 })
但方法一有些繁瑣,JSON.stringify
提供了 replacer
數(shù)組格式參數(shù),可以過濾數(shù)組。
方法二: 借助 replacer
數(shù)組格式
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 => { // 所有的對象按照規(guī)定鍵值 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} ]) // 返回結(jié)果 [{},{"x":1},{"a":1},{"x":1,"a":1},{"x":1,"a":1,"b":1}]
看完了這篇文章,相信你對“javascript中JSON.stringify如何使用”有了一定的了解,如果想了解更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!