學習JSON.stringify的9大特性和轉換規(guī)則
一、JSON.stringify介紹
JSON.stringify()
方法將一個 JavaScript
對象或值轉換為 JSON 字符串,如果指定了一個 replacer
函數(shù),則可以選擇性地替換值,或者指定的 replacer 是數(shù)組,則可選擇性地僅包含數(shù)組指定的屬性。
語法:
JSON.stringify(value[, replacer [, space]])
參數(shù):
value
將要序列化成 一個 JSON 字符串的值。
replacer
可選
- 如果該參數(shù)是一個函數(shù),則在序列化過程中,被序列化的值的每個屬性都會經過該函數(shù)的轉換和處理;
- 如果該參數(shù)是一個數(shù)組,則只有包含在這個數(shù)組中的屬性名才會被序列化到最終的 JSON 字符串中;
- 如果該參數(shù)為 null 或者未提供,則對象所有的屬性都會被序列化。
space
可選
- 指定縮進用的空白字符串,用于美化輸出(pretty-print);
- 如果參數(shù)是個數(shù)字,它代表有多少的空格;上限為10。
- 該值若小于1,則意味著沒有空格;
- 如果該參數(shù)為字符串(當字符串長度超過10個字母,取其前10個字母),該字符串將被作為空格;
- 如果該參數(shù)沒有提供(或者為 null),將沒有空格。
返回值:
一個表示給定值的JSON字符串。
異常:
- 當在循環(huán)引用時會拋出異常TypeError ("cyclic object value")(循環(huán)對象值)
- 當嘗試去轉換 BigInt 類型的值會拋出TypeError ("BigInt value can't be serialized in JSON")(BigInt值不能JSON序列化).
基本使用:
注意:
- JSON.stringify可以轉換對象或者值(平常用的更多的是轉換對象)
- 可以指定replacer為函數(shù)選擇性的地替換
- 也可以指定replacer為數(shù)組,可轉換指定的屬性
這里僅僅是MDN上關于JSON.stringify其中最基礎的說明,咱們先打個碼試試這幾個特性
1. 轉換對象
console.log(JSON.stringify({ name: '前端胖頭魚', sex: 'boy' })) // '{"name":"前端胖頭魚","sex":"boy"}'
2. 轉換普通值
console.log(JSON.stringify('前端胖頭魚')) // "前端胖頭魚" console.log(JSON.stringify(1)) // "1" console.log(JSON.stringify(true)) // "true" console.log(JSON.stringify(null)) // "null"
3. 指定replacer函數(shù)
console.log(JSON.stringify({ name: '前端胖頭魚', sex: 'boy', age: 100 }, (key, value) => { ? return typeof value === 'number' ? undefined : value })) // '{"name":"前端胖頭魚","sex":"boy"}'
4. 指定數(shù)組
console.log(JSON.stringify({ name: '前端胖頭魚', sex: 'boy', age: 100 }, [ 'name' ])) // '{"name":"前端胖頭魚"}'
5. 指定space(美化輸出)
console.log(JSON.stringify({ name: '前端胖頭魚', sex: 'boy', age: 100 })) // '{"name":"前端胖頭魚","sex":"boy","age":100}' console.log(JSON.stringify({ name: '前端胖頭魚', sex: 'boy', age: 100 }, null , 2)) /* { ? "name": "前端胖頭魚", ? "sex": "boy", ? "age": 100 } */
二、9大特性要記住
以前僅僅是使用了這個方法,卻沒有詳細了解他的轉換規(guī)則,居然有9個之多。
特性一
undefined、任意的函數(shù)以及symbol值,出現(xiàn)在非數(shù)組對象的屬性值中時在序列化過程中會被忽略 undefined、任意的函數(shù)以及symbol值出現(xiàn)在數(shù)組中時會被轉換成?null。 undefined、任意的函數(shù)以及symbol值被單獨轉換時,會返回 undefined // 1. 對象中存在這三種值會被忽略 console.log(JSON.stringify({ ? name: '前端胖頭魚', ? sex: 'boy', ? // 函數(shù)會被忽略 ? showName () { ? ? console.log('前端胖頭魚') ? }, ? // undefined會被忽略 ? age: undefined, ? // Symbol會被忽略 ? symbolName: Symbol('前端胖頭魚') })) // '{"name":"前端胖頭魚","sex":"boy"}' // 2. 數(shù)組中存在著三種值會被轉化為null console.log(JSON.stringify([ ? '前端胖頭魚', ? 'boy', ? // 函數(shù)會被轉化為null ? function showName () { ? ? console.log('前端胖頭魚') ? }, ? //undefined會被轉化為null ? undefined, ? //Symbol會被轉化為null ? Symbol('前端胖頭魚') ])) // '["前端胖頭魚","boy",null,null,null]' // 3.單獨轉換會返回undefined console.log(JSON.stringify( ? function showName () { ? ? console.log('前端胖頭魚') ? } )) // undefined console.log(JSON.stringify(undefined)) // undefined console.log(JSON.stringify(Symbol('前端胖頭魚'))) // undefined
特性二
布爾值、數(shù)字、字符串的包裝對象在序列化過程中會自動轉換成對應的原始值。
console.log(JSON.stringify([new Number(1), new String("前端胖頭魚"), new Boolean(false)])) // '[1,"前端胖頭魚",false]'
特性三
所有以symbol
為屬性鍵的屬性都會被完全忽略掉,即便 replacer
參數(shù)中強制指定包含了它們。
console.log(JSON.stringify({ ? [Symbol('前端胖頭魚')]: '前端胖頭魚'} ))? // '{}' console.log(JSON.stringify({ ? [ Symbol('前端胖頭魚') ]: '前端胖頭魚', }, (key, value) => { ? if (typeof key === 'symbol') { ? ? return value ? } })) // undefined
特性四
NaN
和 Infinity
格式的數(shù)值及 null 都會被當做 null。
console.log(JSON.stringify({ ? age: NaN, ? age2: Infinity, ? name: null })) // '{"age":null,"age2":null,"name":null}'
特性五
轉換值如果有toJSON()
方法,該方法定義什么值將被序列化。
const toJSONObj = { ? name: '前端胖頭魚', ? toJSON () { ? ? return 'JSON.stringify' ? } } console.log(JSON.stringify(toJSONObj)) // "JSON.stringify"
特性六
Date 日期調用了 toJSON()
將其轉換為了 string
字符串(同Date.toISOString()),因此會被當做字符串處理。
const d = new Date() console.log(d.toJSON()) // 2021-10-05T14:01:23.932Z console.log(JSON.stringify(d)) // "2021-10-05T14:01:23.932Z"
特性七
對包含循環(huán)引用的對象(對象之間相互引用,形成無限循環(huán))執(zhí)行此方法,會拋出錯誤。
let cyclicObj = { ? name: '前端胖頭魚', } cyclicObj.obj = cyclicObj console.log(JSON.stringify(cyclicObj)) // Converting circular structure to JSON
特性八
其他類型的對象,包括Map/Set/WeakMap/WeakSet
,僅會序列化可枚舉的屬性
let enumerableObj = {} Object.defineProperties(enumerableObj, { ? name: { ? ? value: '前端胖頭魚', ? ? enumerable: true ? }, ? sex: { ? ? value: 'boy', ? ? enumerable: false ? }, }) console.log(JSON.stringify(enumerableObj)) // '{"name":"前端胖頭魚"}'
特性九
當嘗試去轉換 BigInt
類型的值會拋出錯誤
const alsoHuge = BigInt(9007199254740991) console.log(JSON.stringify(alsoHuge)) // TypeError: Do not know how to serialize a BigInt
三、手寫一個JSON.stringify
終于重新學完JSON.stringify
的眾多特性啦!咱們根據(jù)這些特性來手寫一個簡單版本的吧(無replacer函數(shù)和space)
源碼實現(xiàn):
const jsonstringify = (data) => { ? // 確認一個對象是否存在循環(huán)引用 ? const isCyclic = (obj) => { ? // 使用Set數(shù)據(jù)類型來存儲已經檢測過的對象 ? let stackSet = new Set() ? let detected = false ? const detect = (obj) => { ? ? // 不是對象類型的話,可以直接跳過 ? ? if (obj && typeof obj != 'object') { ? ? ? return ? ? } ? ? // 當要檢查的對象已經存在于stackSet中時,表示存在循環(huán)引用 ? ? if (stackSet.has(obj)) { ? ? ? return detected = true ? ? } ? ? // 將當前obj存如stackSet ? ? stackSet.add(obj) ? ? for (let key in obj) { ? ? ? // 對obj下的屬性進行挨個檢測 ? ? ? if (obj.hasOwnProperty(key)) { ? ? ? ? detect(obj[key]) ? ? ? } ? ? } ? ? // 平級檢測完成之后,將當前對象刪除,防止誤判 ? ? /* ? ? ? 例如:對象的屬性指向同一引用,如果不刪除的話,會被認為是循環(huán)引用 ? ? ? let tempObj = { ? ? ? ? name: '前端胖頭魚' ? ? ? } ? ? ? let obj4 = { ? ? ? ? obj1: tempObj, ? ? ? ? obj2: tempObj ? ? ? } ? ? */ ? ? stackSet.delete(obj) ? } ? detect(obj) ? return detected } ? // 特性七: ? // 對包含循環(huán)引用的對象(對象之間相互引用,形成無限循環(huán))執(zhí)行此方法,會拋出錯誤。 ? if (isCyclic(data)) { ? ? throw new TypeError('Converting circular structure to JSON') ? } ? // 特性九: ? // 當嘗試去轉換 BigInt 類型的值會拋出錯誤 ? if (typeof data === 'bigint') { ? ? throw new TypeError('Do not know how to serialize a BigInt') ? } ? const type = typeof data ? const commonKeys1 = ['undefined', 'function', 'symbol'] ? const getType = (s) => { ? ? return Object.prototype.toString.call(s).replace(/\[object (.*?)\]/, '$1').toLowerCase() ? } ? // 非對象 ? if (type !== 'object' || data === null) { ? ? let result = data ? ? // 特性四: ? ? // NaN 和 Infinity 格式的數(shù)值及 null 都會被當做 null。 ? ? if ([NaN, Infinity, null].includes(data)) { ? ? ? result = 'null' ? ? ? // 特性一: ? ? ? // `undefined`、`任意的函數(shù)`以及`symbol值`被`單獨轉換`時,會返回 undefined ? ? } else if (commonKeys1.includes(type)) { ? ? ? // 直接得到undefined,并不是一個字符串'undefined' ? ? ? return undefined ? ? } else if (type === 'string') { ? ? ? result = '"' + data + '"' ? ? } ? ? return String(result) ? } else if (type === 'object') { ? ? // 特性五: ? ? // 轉換值如果有 toJSON() 方法,該方法定義什么值將被序列化 ? ? // 特性六: ? ? // Date 日期調用了 toJSON() 將其轉換為了 string 字符串(同Date.toISOString()),因此會被當做字符串處理。 ? ? if (typeof data.toJSON === 'function') { ? ? ? return jsonstringify(data.toJSON()) ? ? } else if (Array.isArray(data)) { ? ? ? let result = data.map((it) => { ? ? ? ? // 特性一: ? ? ? ? // `undefined`、`任意的函數(shù)`以及`symbol值`出現(xiàn)在`數(shù)組`中時會被轉換成?`null` ? ? ? ? return commonKeys1.includes(typeof it) ? 'null' : jsonstringify(it) ? ? ? }) ? ? ? return `[${result}]`.replace(/'/g, '"') ? ? } else { ? ? ? // 特性二: ? ? ? // 布爾值、數(shù)字、字符串的包裝對象在序列化過程中會自動轉換成對應的原始值。 ? ? ? if (['boolean', 'number'].includes(getType(data))) { ? ? ? ? return String(data) ? ? ? } else if (getType(data) === 'string') { ? ? ? ? return '"' + data + '"' ? ? ? } else { ? ? ? ? let result = [] ? ? ? ? // 特性八 ? ? ? ? // 其他類型的對象,包括 Map/Set/WeakMap/WeakSet,僅會序列化可枚舉的屬性 ? ? ? ? Object.keys(data).forEach((key) => { ? ? ? ? ? // 特性三: ? ? ? ? ? // 所有以symbol為屬性鍵的屬性都會被完全忽略掉,即便 replacer 參數(shù)中強制指定包含了它們。 ? ? ? ? ? if (typeof key !== 'symbol') { ? ? ? ? ? ? const value = data[key] ? ? ? ? ? ? // 特性一 ? ? ? ? ? ? // `undefined`、`任意的函數(shù)`以及`symbol值`,出現(xiàn)在`非數(shù)組對象`的屬性值中時在序列化過程中會被忽略 ? ? ? ? ? ? if (!commonKeys1.includes(typeof value)) { ? ? ? ? ? ? ? result.push(`"${key}":${jsonstringify(value)}`) ? ? ? ? ? ? } ? ? ? ? ? } ? ? ? ? }) ? ? ? ? return `{${result}}`.replace(/'/, '"') ? ? ? } ? ? } ? } }
測試一把:
// 1. 測試一下基本輸出 console.log(jsonstringify(undefined)) // undefined? console.log(jsonstringify(() => { })) // undefined console.log(jsonstringify(Symbol('前端胖頭魚'))) // undefined console.log(jsonstringify((NaN))) // null console.log(jsonstringify((Infinity))) // null console.log(jsonstringify((null))) // null console.log(jsonstringify({ ? name: '前端胖頭魚', ? toJSON() { ? ? return { ? ? ? name: '前端胖頭魚2', ? ? ? sex: 'boy' ? ? } ? } })) // {"name":"前端胖頭魚2","sex":"boy"} // 2. 和原生的JSON.stringify轉換進行比較 console.log(jsonstringify(null) === JSON.stringify(null)); // true console.log(jsonstringify(undefined) === JSON.stringify(undefined)); // true console.log(jsonstringify(false) === JSON.stringify(false)); // true console.log(jsonstringify(NaN) === JSON.stringify(NaN)); // true console.log(jsonstringify(Infinity) === JSON.stringify(Infinity)); // true let str = "前端胖頭魚"; console.log(jsonstringify(str) === JSON.stringify(str)); // true let reg = new RegExp("\w"); console.log(jsonstringify(reg) === JSON.stringify(reg)); // true let date = new Date(); console.log(jsonstringify(date) === JSON.stringify(date)); // true let sym = Symbol('前端胖頭魚'); console.log(jsonstringify(sym) === JSON.stringify(sym)); // true let array = [1, 2, 3]; console.log(jsonstringify(array) === JSON.stringify(array)); // true let obj = { ? name: '前端胖頭魚', ? age: 18, ? attr: ['coding', 123], ? date: new Date(), ? uni: Symbol(2), ? sayHi: function () { ? ? console.log("hello world") ? }, ? info: { ? ? age: 16, ? ? intro: { ? ? ? money: undefined, ? ? ? job: null ? ? } ? }, ? pakingObj: { ? ? boolean: new Boolean(false), ? ? string: new String('前端胖頭魚'), ? ? number: new Number(1), ? } } console.log(jsonstringify(obj) === JSON.stringify(obj))? // true console.log((jsonstringify(obj))) // {"name":"前端胖頭魚","age":18,"attr":["coding",123],"date":"2021-10-06T14:59:58.306Z","info":{"age":16,"intro":{"job":null}},"pakingObj":{"boolean":false,"string":"前端胖頭魚","number":1}} console.log(JSON.stringify(obj)) // {"name":"前端胖頭魚","age":18,"attr":["coding",123],"date":"2021-10-06T14:59:58.306Z","info":{"age":16,"intro":{"job":null}},"pakingObj":{"boolean":false,"string":"前端胖頭魚","number":1}} // 3. 測試可遍歷對象 let enumerableObj = {} Object.defineProperties(enumerableObj, { ? name: { ? ? value: '前端胖頭魚', ? ? enumerable: true ? }, ? sex: { ? ? value: 'boy', ? ? enumerable: false ? }, }) console.log(jsonstringify(enumerableObj)) // {"name":"前端胖頭魚"} // 4. 測試循環(huán)引用和Bigint let obj1 = { a: 'aa' } let obj2 = { name: '前端胖頭魚', a: obj1, b: obj1 } obj2.obj = obj2 console.log(jsonstringify(obj2)) // TypeError: Converting circular structure to JSON console.log(jsonStringify(BigInt(1))) // TypeError: Do not know how to serialize a BigInt
通過上面測試可以看出,jsonstringify基本和JSON.stringify表現(xiàn)一致,
到此這篇關于學習JSON.stringify的9大特性和轉換規(guī)則的文章就介紹到這了,更多相關JSON.stringify內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
基于JavaScript實現(xiàn)動態(tài)創(chuàng)建表格和增加表格行數(shù)
這篇文章主要介紹了基于JavaScript實現(xiàn)動態(tài)創(chuàng)建表格和增加表格行數(shù)的相關資料,需要的朋友可以參考下2015-12-12Javascript實現(xiàn)圖片輪播效果(二)圖片序列節(jié)點的控制實現(xiàn)
這篇文章主要介紹了Javascript實現(xiàn)圖片輪播效果(二)圖片序列節(jié)點的控制實現(xiàn)的相關資料,需要的朋友可以參考下2016-02-02一個收集圖片的bookmarlet(js 刷新頁面中的圖片)
這個代碼是用來在新的頁面內顯示當前頁面內的所有圖片的。2010-05-05js求數(shù)組中全部數(shù)字可拼接出的最大整數(shù)示例代碼
這篇文章主要給大家介紹了利用js如何求數(shù)組中全部數(shù)字可拼接出的最大整數(shù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考借鑒,下面隨著小編一起來學習學習吧。2017-08-08JavaScript創(chuàng)建對象的七種經典方式分享
JavaScript 創(chuàng)建對象的方式有很多,通過 Object 構造函數(shù)或對象字面量的方式也可以創(chuàng)建單個對象,顯然這兩種方式會產生大量的重復代碼,并不適合量產。本文介紹了七種非常經典的創(chuàng)建對象的方式,希望對大家有所幫助2022-11-11