自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

比JSON.stringify快兩倍的fast-json-stringify

開發(fā) 前端
相信大家對(duì)JSON.stringify并不陌生,通常在很多場(chǎng)景下都會(huì)用到這個(gè)API,最常見(jiàn)的就是HTTP請(qǐng)求中的數(shù)據(jù)傳輸, 因?yàn)镠TTP 協(xié)議是一個(gè)文本協(xié)議,傳輸?shù)母袷蕉际亲址覀冊(cè)诖a中常常操作的是 JSON 格式的數(shù)據(jù),所以我們需要在返回響應(yīng)數(shù)據(jù)前將 JSON 數(shù)據(jù)序列化為字符串。

前言?

相信大家對(duì)JSON.stringify并不陌生,通常在很多場(chǎng)景下都會(huì)用到這個(gè)API,最常見(jiàn)的就是HTTP請(qǐng)求中的數(shù)據(jù)傳輸, 因?yàn)镠TTP 協(xié)議是一個(gè)文本協(xié)議,傳輸?shù)母袷蕉际亲址?,但我們?cè)诖a中常常操作的是 JSON 格式的數(shù)據(jù),所以我們需要在返回響應(yīng)數(shù)據(jù)前將 JSON 數(shù)據(jù)序列化為字符串。但大家是否考慮過(guò)使用JSON.stringify可能會(huì)帶來(lái)性能風(fēng)險(xiǎn)??,或者說(shuō)有沒(méi)有一種更快的stringify方法。

JSON.stringify的性能瓶頸?

由于 JavaScript 是動(dòng)態(tài)語(yǔ)言,它的變量類型只有在運(yùn)行時(shí)才能確定,所以 JSON.stringify 在執(zhí)行過(guò)程中要進(jìn)行大量的類型判斷,對(duì)不同類型的鍵值做不同的處理。由于不能做靜態(tài)分析,執(zhí)行過(guò)程中的類型判斷這一步就不可避免,而且還需要一層一層的遞歸,循環(huán)引用的話還有爆棧的風(fēng)險(xiǎn)。

我們知道,JSON.string的底層有兩個(gè)非常重要的步驟:

  • 類型判斷
  • 遞歸遍歷

既然是這樣,我們可以先來(lái)對(duì)比一下JSON.stringify與普通遍歷的性能,看看類型判斷這一步到底是不是影響JSON.stringify性能的主要原因。

JSON.stringify 與遍歷對(duì)比

const obj1 = {}, obj2 = {}
for(let i = 0; i < 1000000; i++) {
obj1[i] = i
obj2[i] = i
}

function fn1 () {
console.time('jsonStringify')
const res = JSON.stringify(obj1) === JSON.stringify(obj2)
console.timeEnd('jsonStringify')
}

function fn2 () {
console.time("for");
const res = Object.keys(obj1).every((key) => {
if (obj2[key] || obj2[key] === 0) {
return true;
} else {
return false;
}
});
console.timeEnd("for");
}
fn1()
fn2()

從結(jié)果來(lái)看,兩者的性能差距在4倍左右,那就證明JSON.string的類型判斷這一步還是非常耗性能的。如果JSON.stringify能夠跳過(guò)類型判斷這一步是否對(duì)類型判斷有幫助呢?

定制化更快的JSON.stringify

基于上面的猜想,我們可以來(lái)嘗試實(shí)現(xiàn)一下:

現(xiàn)在我們有下面這個(gè)對(duì)象

const obj = {
name: '南玖',
hobby: 'fe',
age: 18,
chinese: true
}

上面這個(gè)對(duì)象經(jīng)過(guò)JSON.stringify處理后是這樣的:

JSON.stringify(obj)
// {"name":"南玖","hobby":"fe","age":18,"chinese":true}

現(xiàn)在假如我們已經(jīng)提前知道了這個(gè)對(duì)象的結(jié)構(gòu)

  • 鍵名不變
  • 鍵值類型不變

這樣的話我們就可以定制一個(gè)更快的JSON.stringify方法

function myStringify(obj) {
return `{"name":"${obj.name}","hobby":"${obj.hobby}","age":${obj.age},"chinese":${obj.chinese}}`
}

console.log(myStringify(obj) === JSON.stringify(obj)) // true

這樣也能夠得到JSON.stringify一樣的效果,前提是你已經(jīng)知道了這個(gè)對(duì)象的結(jié)構(gòu)。

事實(shí)上,這是許多JSON.stringify加速庫(kù)的通用手段:

  • 需要先確定對(duì)象的結(jié)構(gòu)信息
  • 再根據(jù)結(jié)構(gòu)信息,為該種結(jié)構(gòu)的對(duì)象創(chuàng)建“定制化”的stringify方法
  • 內(nèi)部實(shí)現(xiàn)依然是這種字符串拼接

更快的fast-json-stringify?

fast-json-stringify 需要JSON Schema Draft 7輸入來(lái)生成快速stringify函數(shù)。

這也就是說(shuō)fast-json-stringify?這個(gè)庫(kù)是用來(lái)給我們生成一個(gè)定制化的stringily函數(shù),從而來(lái)提升stringify的性能。

這個(gè)庫(kù)的GitHub簡(jiǎn)介上寫著「比 JSON.stringify() 快 2 倍」,其實(shí)它的優(yōu)化思路跟我們上面那種方法是一致的,也是一種定制化stringify方法。

語(yǔ)法

const fastJson = require('fast-json-stringify')
const stringify = fastJson(mySchema, {
schema: { ... },
ajv: { ... },
rounding: 'ceil'
})
  • schema: $ref 屬性引用的外部模式。
  • ajv: ajv v8 實(shí)例對(duì)那些需要ajv.
  • rounding: 設(shè)置當(dāng)integer類型不是整數(shù)時(shí)如何舍入。
  • largeArrayMechanism:設(shè)置應(yīng)該用于處理大型(默認(rèn)情況下20000或更多項(xiàng)目)數(shù)組的機(jī)制

scheme

這其實(shí)就是我們上面所說(shuō)的定制化對(duì)象結(jié)構(gòu),比如還是這個(gè)對(duì)象:

const obj = {
name: '南玖',
hobby: 'fe',
age: 18,
chinese: true
}

它的JSON scheme是這樣的:

{
type: "object",
properties: {
name: {type: "string"},
hobby: {type: "string"},
age: {type: "integer"},
chinese: {type: 'boolean'}
},
required: ["name", "hobby", "age", "chinese"]
}

AnyOf 和 OneOf

當(dāng)然除了這種簡(jiǎn)單的類型定義,JSON Schema 還支持一些條件運(yùn)算,比如字段類型可能是字符串或者數(shù)字,可以用 oneOf 關(guān)鍵字:

"oneOf": [
{
"type": "string"
},
{
"type": "number"
}
]

fast-json-stringify?支持JSON 模式定義的「anyOf」和「oneOf關(guān)鍵字?!箖烧叨急仨毷且唤M有效的 JSON 模式。不同的模式將按照指定的順序進(jìn)行測(cè)試。stringify在找到匹配項(xiàng)之前必須嘗試的模式越多,速度就越慢。

anyOf和oneOf使用ajv作為 JSON 模式驗(yàn)證器來(lái)查找與數(shù)據(jù)匹配的模式。這對(duì)性能有影響——只有在萬(wàn)不得已時(shí)才使用它。

關(guān)于 JSON Schema 的完整定義,可以參考 Ajv 的文檔,Ajv 是一個(gè)流行的 JSON Schema驗(yàn)證工具,性能表現(xiàn)也非常出眾。

當(dāng)我們可以提前確定一個(gè)對(duì)象的結(jié)構(gòu)時(shí),可以將其定義為一個(gè) Schema,這就相當(dāng)于提前告訴 stringify 函數(shù),需序列化的對(duì)象的數(shù)據(jù)結(jié)構(gòu),這樣它就可以不必再在運(yùn)行時(shí)去做類型判斷,這就是這個(gè)庫(kù)提升性能的關(guān)鍵所在。

簡(jiǎn)單使用?

const fastJson = require('fast-json-stringify')
const stringify = fastJson({
title: 'myObj',
type: 'object',
properties: {
name: {
type: 'string'
},
hobby: {
type: 'string'
},
age: {
description: 'Age in years',
type: 'integer'
},
chinese: {
type: 'boolean'
}
}
})

console.log(stringify({
name: '南玖',
hobby: 'fe',
age: 18,
chinese: true
}))
//

生成 stringify 函數(shù)

fast-json-stringify是跟我們傳入的scheme來(lái)定制化生成一個(gè)stringily函數(shù),上面我們了解了怎么為我們對(duì)象定義一個(gè)scheme結(jié)構(gòu),接下來(lái)我們?cè)賮?lái)了解一下如何生成stringify。

這里有一些工具方法還是值得了解一下的:

const asFunctions = `
function $asAny (i) {
return JSON.stringify(i)
}

function $asNull () {
return 'null'
}

function $asInteger (i) {
if (isLong && isLong(i)) {
return i.toString()
} else if (typeof i === 'bigint') {
return i.toString()
} else if (Number.isInteger(i)) {
return $asNumber(i)
} else {
return $asNumber(parseInteger(i))
}
}

function $asNumber (i) {
const num = Number(i)
if (isNaN(num)) {
return 'null'
} else {
return '' + num
}
}

function $asBoolean (bool) {
return bool && 'true' || 'false'
}

// 省略了一些其他類型......
`

從上面我們可以看到,「如果你使用的是 any 類型,它內(nèi)部依然還是用的 JSON.stringify?!顾晕覀?cè)谟肨S進(jìn)行開發(fā)時(shí)應(yīng)避免使用 any 類型,因?yàn)槿绻腔?nbsp;TS interface? 生成 JSON Schema 的話,使用 any 也會(huì)影響到 JSON 序列化的性能。

然后就會(huì)根據(jù) scheme 定義的具體內(nèi)容生成 stringify 函數(shù)的具體代碼。而生成的方式也比較簡(jiǎn)單:通過(guò)遍歷 scheme,根據(jù)不同數(shù)據(jù)類型調(diào)用上面不同的工具函數(shù)來(lái)進(jìn)行字符串拼接。感興趣的同學(xué)可以在GitHub上查看源碼

總結(jié)?

事實(shí)上fast-json-stringify只是通過(guò)靜態(tài)的結(jié)構(gòu)信息將優(yōu)化與分析前置了,通過(guò)開發(fā)者定義的scheme內(nèi)容可以提前知道對(duì)象的數(shù)據(jù)結(jié)構(gòu),然后會(huì)生成一個(gè)stringify函數(shù)供開發(fā)者調(diào)用,該函數(shù)內(nèi)部其實(shí)就是做了字符串的拼接。

  • 開發(fā)者定義 Object 的JSON scheme
  • stringify 庫(kù)根據(jù) scheme 生成對(duì)應(yīng)的模版方法,模版方法里會(huì)對(duì)屬性與值進(jìn)行字符串拼接
  • 最后開發(fā)者調(diào)用生成的stringify 方法
責(zé)任編輯:華軒 來(lái)源: 前端南玖
相關(guān)推薦

2021-05-06 05:30:33

JSONstringify()parse()

2019-06-11 15:25:03

JSON性能前端

2021-12-11 18:59:35

JavascriptJSON應(yīng)用

2020-03-29 20:16:09

JavaScript前端技術(shù)

2020-05-25 14:37:31

JSON.string前端秘密特性

2021-12-22 09:08:39

JSON.stringJavaScript字符串

2024-09-30 11:08:18

JSON局限性數(shù)據(jù)

2022-08-31 22:50:13

JavaScript函數(shù)JSON

2024-03-25 00:10:00

JSON后端開發(fā)

2025-04-21 10:43:21

2023-01-17 16:25:18

前端開發(fā)JSON

2022-03-10 09:11:33

JavaScrip開發(fā)JSON

2024-05-08 08:32:25

架構(gòu)

2009-06-03 09:16:54

FirefoxMozilla瀏覽器

2023-05-12 08:11:58

JavaScriptJSON克隆

2023-05-08 09:00:46

JSON深拷貝對(duì)象

2024-11-06 08:32:02

JavaScriptTypeScript數(shù)據(jù)結(jié)構(gòu)

2024-01-05 07:46:15

JS克隆對(duì)象JSON

2022-04-29 10:17:51

數(shù)據(jù)庫(kù)Group-IB數(shù)據(jù)庫(kù)安全

2022-10-27 08:31:31

架構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)