创建自己的json stringify简化实现

You may already be familiar with the function JSON.stringify, which can be useful when comparing objects, implementing RESTFUL APIs or simply deep cloning a javascript object (although, it’s not recommended).

您可能已经熟悉JSON.stringify函数,该函数在比较对象,实现RESTFUL API或只是深度克隆javascript对象时很有用(尽管不建议这样做)。

In this article, we are going to talk about how to create our own simplified version of this method, and learn how to improve its implementation step by step, covering more and more cases as we progress.

在本文中,我们将讨论如何创建此方法的自己的简化版本,并逐步了解如何逐步改进其实现,并讨论越来越多的情况。

If you are not familiar with this function, let’s take a look at what MDN has to say about it:

如果您不熟悉此功能,那么让我们看一下MDN对此有何评论:

The JSON.stringify() method converts a JavaScript object or value to a JSON string, optionally replacing values if a replacer function is specified or optionally including only the specified properties if a replacer array is specified.

JSON.stringify()方法将JavaScript对象或值转换为JSON字符串,如果指定了replacer函数,则可以选择替换值,如果指定了replacer数组,则可以仅包括指定的属性。

Its syntax can be written like this:

它的语法可以这样写:

JSON.stringify(value[, replacer[, space]])

Where “value” is the object or value we want to convert to a string. We can leave out the other 2 parameters for this article to make it easier.

其中“值”是我们要转换为字符串的对象或值。 我们可以在本文中省略其他两个参数,以使其变得更容易。

测试数据 (Testing data)

Consider this case:

考虑这种情况:

const sampleObj = {
"name": "Juan",
"age": 29,
"address": {
"street": "Street 1",
"number": 3
}
}

If we apply the origin JSON.stringify() function to that object, this is what we get:

如果我们将原始JSON.stringify()函数应用于该对象,则会得到以下结果:

{"name":"Juan","age":29,"address":{"street":"Street 1","number":3}}

As you can see, it’s fairly easy. It adds double quotes to the attributes, and if the value is a string it adds them too. For this particular example, we’ll only tackle those 3 data types: Number, String and Object. We’ll leave out Function, dates, undefined values and so, just to keep it simple, although, I recommend you read the documentation of JSON.stringify to see how it behaves with those types and others.

如您所见,这相当容易。 它将双引号添加到属性,如果该值是字符串,它也会添加双引号。 对于此特定示例,我们将仅处理这三种数据类型:数字,字符串和对象。 为了简单JSON.stringify ,我们将省略函数,日期,未定义的值等,但是,我建议您阅读JSON.stringify的文档,以了解其在这些类型和其他类型下的行为。

功能 (The function)

Let’s start with it:

让我们开始吧:

function stringify(obj) {
let objString = ''; // We add the opening curly brace
objString += '{'; for (const key in obj) {
const value = obj[key];
objString += `"${key}":`;
if (typeof obj[key] === 'object') {
objString += `${stringify(value)}`;
} else if (typeof value === 'string') {
objString += `"${value}"`;
} else if (typeof obj[key] === 'number') {
objString += `${value}`;
}
// We add the comma
objString += `,`;
} // We add the closing curly brace
objString += '}'; return objString;
}

As you can see, what we are doing is to manually add the curly braces and then iterate through each one of the keys in the object, and depending its type, we print its value after printing the key wrapped in double quotes. In case the key contains an object, we use recursion to call the stringify function again and repeat the process.

如您所见,我们正在做的是手动添加花括号,然后遍历对象中的每个键,并根据其类型,在打印用双引号引起来的键之后打印其值。 如果键包含一个对象,我们使用递归再次调用stringify函数并重复该过程。

If we execute that function with our sampleObj, this is the result:

如果我们使用sampleObj执行该函数,则结果如下:

{"name":"Juan","age":29,"address":{"street":"Street 1","number":3,},}

Which is good for a start, but if you look closely, you’ll see all the trailing commas ruining the beauty of the JSON string. Bad!

这是一个很好的开始,但是如果仔细观察,您会发现所有结尾的逗号都破坏了JSON字符串的美观。 坏!

Let’s try to fix this:

让我们尝试解决此问题:

function stringify(obj) {
let objString = '';
// We get the last key of this object
const lastKey = Object.keys(obj).pop(); // We add the first curly brace
objString += '{'; for (const key in obj) {
const value = obj[key];
objString += `"${key}":`;
if (typeof obj[key] === 'object') {
objString += `${stringify(value)}`;
} else if (typeof value === 'string') {
objString += `"${value}"`;
} else if (typeof obj[key] === 'number') {
objString += `${value}`;
}
// We add the comma
if (key !== lastKey) {
objString += ',';
}
} // We add the last curly brace
objString += '}'; return objString;
}

Whoa, that was easy. We are just getting the last key for the object, and when iterating, if the current element is belongs to the last key, we don’t add the comma. Can you think of a better way of doing it? This one will be enough for now.

哇,那很容易。 我们只是获取对象的最后一个键,并且在迭代时,如果当前元素属于最后一个键,则不添加逗号。 您能想到一种更好的方法吗? 目前,这已经足够了。

Let’s see the result of this function using again our sampleObj:

让我们再次使用sampleObj来查看此函数的结果:

{"name":"Juan","age":29,"address":{"street":"Street 1","number":3}}

Nice! That looks like it’s done now, but… What happens if you send a string or a number instead of an object? Let’s see how does JSON.stringify behaves:

真好! 看起来现在已经完成了,但是……如果您发送字符串或数字而不是对象,会发生什么? 让我们看看JSON.stringify行为:

> JSON.stringify("test");
"test"
> JSON.stringify(42);
42

If we were to execute our function with those values, we would get a different output, let’s see:

如果我们要使用这些值执行函数,我们将获得不同的输出,让我们看看:

> stringify("test");
{"0":"t","1":"e","2":"s","3":"t"}> stringify(42);
{}

Let’s try to fix that, we will need to check the type before iterating the object. This is how it would look like:

让我们尝试解决这个问题,我们需要在迭代对象之前检查类型。 它是这样的:

function stringify(value) {
const lastKey = Object.keys(value).pop();
let objString = ''; if (typeof value === 'object') {
// We add the first curly brace
objString += '{'; for (const key in value) {
objString += `"${key}":${stringify(value[key])}`;
// We add the comma
if (key !== lastKey) {
objString += ',';
}
} // We add the last curly brace
objString += '}';
} else if (typeof value === 'string') {
objString += `"${value}"`;
} else if (typeof value === 'number') {
objString += `${value}`;
} return objString;
}

Done. As you can see, we moved the type check outside of the for iterator for the current object. We also changed the name of the parameter from obj to value because now it can be a string or a number too.

做完了 如您所见,我们将类型检查移到了当前对象的for迭代器之外。 我们还将参数的名称从obj更改为value因为现在它也可以是字符串或数字。

Let’s try it with some examples:

让我们尝试一些示例:

> stringify("test");
"test"> stringify(42);
42> stringify(sampleObj)
{"name":"Juan","age":29,"address":{"street":"Street 1","number":3}}

That looks like it, it now works for simple objects, numbers and strings, and we reached the end of this article, but I’ll leave a few questions for you to think and try to implement your own solutions:

看起来像这样,它现在适用于简单的对象,数字和字符串,并且我们已经到了本文的结尾,但是我将留下一些问题供您思考并尝试实现自己的解决方案:

循环参考问题 (The circular reference problem)

If you are familiar with serialization methods, you may have heard of a “circular reference” or maybe an “infinite loop”. This happens when you’re trying to serialize an object that has a reference to another object, that has a reference to the first one! Sounds crazy? Let’s see it.

如果您熟悉序列化方法,则可能听说过“循环引用”或“无限循环”。 当您尝试序列化一个引用另一个对象的对象,该对象引用第一个对象时,就会发生这种情况! 听起来疯了吗? 让我们来看看它。

const sampleObj = {
name: 'Juan',
age: 29,
address: {
street: 'Street 1',
number: 3,
}
};const objTwo = {
name: 'Two',
inside: sampleObj
};sampleObj.outside = objTwo;

If we try to send our sampleObj now to our stringify function, we’ll get an error. Most browsers will say:

如果我们现在尝试将sampleObj发送到我们的stringify函数,则会收到错误消息。 大多数浏览器会说:

Uncaught RangeError: Maximum call stack size exceeded

That happens because we are using recursion when an object is found, and therefore this will create an infinite loop. Can you think of any solution to that? What would you do? We may cover that on another article, but it would be nice if you can provide a solution to that problem, or even share your own stringify function.

发生这种情况是因为在找到对象时我们正在使用递归,因此这将创建一个无限循环。 您能想到解决方案吗? 你会怎么做? 我们可能会在另一篇文章中进行介绍,但是如果您可以提供该问题的解决方案,甚至共享您自己的stringify函数,那也很好。

下一步 (Next Steps)

  • Solve the circular reference problem.

    解决循环参考问题。
  • Add support for “boolean” values.

    添加对“布尔”值的支持。
  • Add support for Date and other objects implementing the toJson() function.

    添加对Date和其他实现toJson()函数的对象的支持。

结论 (Conclusion)

Even though JSON.stringify is supported by most browsers, it’s good for you to play and try to see how the native methods are implemented. It could help you understand what happens behind the scenes, and maybe one day you’ll improve one of the existing functions and your code will be included in the next EcmaScript release, who knows?

即使大多数浏览器都支持JSON.stringify ,但它对您玩游戏还是尝试看看如何实现本机方法还是JSON.stringify 。 它可以帮助您了解幕后发生的事情,也许有一天您将改进现有功能之一,并且您的代码将包含在下一EcmaScript版本中,谁知道呢?

进一步阅读 (Further reads)

翻译自: https://medium.com/@juandpr/creating-your-own-simplified-implementation-of-json-stringify-ed8e50b9144a

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值