JSON.stringify是我们经常使用的一个方法,它的主要功能是将JavaScript值和对象转换成字符串。如:JSON.stringify({foo:"bar"});//=>'{"foo":"bar"}'JSON.stringify(123);//=>'123'但是JS很多地方有问题,这个函数也不例外。我们可能会想象一个名为“stringify”的函数总是返回一个字符串……但它不会!例如,如果您尝试对undefined进行字符串化,它会返回undefined,而不是字符串。JSON.stringify(undefined);//=>undefined下面我分两部分讲:?undefined、任意函数和符号值在序列化时将被忽略(出现在非数组对象的属性值中时)或转换为null(出现在数组中时)。单独转换函数,undefined时,会返回undefined。在包含循环引用(对象相互引用,形成死循环)的对象上执行该方法会抛出错误。我认为JSON.stringify可以返回字符串以外的东西是令人惊讶的。但是在6种情况下它可以返回undefined:尝试在顶层序列化undefined返回undefined。JSON.stringify(undefined);//=>undefined尝试序列化函数也会返回undefined。这适用于常规函数、箭头函数、异步函数和生成器函数。JSON.stringify(functionfoo(){});//=>undefinedJSON.stringify(()=>{});//=>undefinedfunctionbar(){}bar.someProperty=123;JSON.stringify(bar);//=>undefined尝试序列化一个符号也会返回undefined。JSON.stringify(Symbol("computerswereamistake"));//=>undefined在浏览器中,试图序列化被丢弃的文档。all也会返回undefined。//=>undefined这只影响浏览器,因为document.all在其他环境中不可用,例如Node。将运行具有toJSON函数的对象,而不是尝试正常序列化它们。但如果toJSON返回上述值之一,试图在顶层对其进行序列化将导致JSON.stringify返回undefined。JSON.stringify({toJSON:()=>undefined});//=>undefinedJSON.stringify({ignored:true,toJSON:()=>undefined});//=>undefinedJSON.stringify({toJSON:()=>Symbol("heya")});//=>undefined你可以传递第二个参数,叫做“replacer”,它可以改变序列化的逻辑。如果此函数为顶级返回上述值之一,JSON.stringify将返回undefined。JSON.stringify({ignored:true},()=>undefined);//=>undefinedJSON.stringify(["ignored"],()=>Symbol("hello"));//=>undefined需要注意不幸的是,其中许多事情实际上只影响顶层的序列化。比如JSON.stringify({foo:undefined}),返回字符串“{}”,这并不奇怪。我还想提一下,TypeScript的类型定义在这里是不正确的。例如下面的代码类型验证可以通过:constresult:string=JSON.stringify(undefined);在第2部分中,我们将讨论如何更新TypeScript定义以确保其正确性。JSON.stringify也可能遇到导致它抛出错误的问题。在正常情况下,可能会发生四种情况:循环引用导致抛出类型错误。constb={a};a.b=b;JSON.stringify(a);//=>TypeError:cyclicobjectvalue注意这些错误信息在不同的浏览器中可能不一样,比如Firefox和Chrome的错误信息是不一样的.BigInts不能用JSON.stringify序列化,这些也会导致TypeError。JSON.stringify(12345678987654321n);//=>TypeError:BigIntvaluecan'tbeserializedinJSONJSON.stringify({foo:456n});//=>TypeError:BigIntvaluecan'tbeserializedinJSONobjectwithtoJSON函数将运行。如果这些函数抛出错误,它将冒泡到调用者。constobj={foo:"ignored",toJSON(){thrownewError("Ohno!");},};JSON.stringify(obj);//=>错误:Ohno!您可以传递第二个参数,称为replacer。如果这个函数抛出错误,它就会冒泡。JSON.stringify({},()=>{thrownewError("Uhoh!");});//=>错误:Uhoh!现在我们已经看到JSON.stringify不返回字符串,接下来,我们来看看如何避免这些问题。如何避免这些问题对于如何解决这些陷阱没有通用的方法,所以这里有一些常见的情况。处理循环引用从个人经验来看,传递循环引用时JSON.stringify是最容易出错的。如果这对您来说是一个常见问题,我推荐json-stringify-safe包,它可以很好地处理这种情况。conststringifySafe=require("json-stringify-safe");consta={};constb={a};a.b=b;JSON.stringify(a);//=>TypeError:cyclicobjectvaluestringifySafe(a);//=>'{"b":{"a":[Circular~]"}}'包装您可能希望使用您自己的自定义函数包装JSON.stringify。你可以决定你想要它做什么。错误应该冒出来吗?如果JSON.stringify返回未定义怎么办?例如,SignalDesktop有一个名为reallyJsonStringify的函数,它总是返回一个字符串用于调试目的。像这个函数reallyJsonStringify(value){letresult;try{result=JSON.stringify(value);}catch(_err){//如果有错误,就当做`undefined`.result=undefined;}if(typeofresult==="string"){//是一个字符串,sowe'regood.returnresult;}else{//Convertittoastring.returnObject.prototype.toString.call(value);}}TypeScript类型注意事项如果你已经在使用TypeScript,你可能会惊讶的发现,TypeScript官方对JSON.stringify的定义在这里并不正确。它们实际上是这样的//注意:这是简化的interfaceJSON{//...stringify(value:any):string;}不幸的是,这是一个长期存在的问题,没有完美的解决方案。您可以尝试修补JSON.stringify的类型,但每种解决方案都有某些缺点。我建议使用自定义类型和.例如SignalDesktop的reallyJsonStringify的模板:functionreallyJsonStringify(value:unknown):string{//...summaryJSON.stringify有时返回undefined而不是字符串JSON.stringify有时会抛出错误我们可以使用不同的方式来包装函数解决这个问题希望这篇文章能让你对JSON.stringify有一个更全面的了解。作者:BlackLivesMatter译者:前端小智来源:devinduct原文:https://evanhahn.com/when-stringify-doesnt-return-a-string
