背景
记之前遇见的一道面试题,让现场写出 JavaScript 中stringify
函数的实现。首先写一下自己最开始的思路,然后针对里面的一些问题进行逐步修改,并且引出对 JSON 这种轻量级数据传输格式所拥有数据类型的学习与思考。
一、最初实现
首先给定题目
将一个 JSON 格式对象转换为字符串,转换后的结果可以通过
JSON.parse()
方法将该字符串重新转换为一个 JSON 对象。
先简化问题,将数据类型简单划分(并不正确,下文会给描述)为:对象(Object)、数组(Array)和字符串(String)这三种类型。对于 String,只需要简单的对它进行toString()
调用,并包裹在"
中处理,对于前两者则需要采取不同操作。考虑到他们的子元素也是 JSON 对象,定义具有递归性,所以代码也采用递归来实现。最初代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
function isArray () {} function isObject () {} function stringify (obj) { let ret = ''; if (!isObject(obj) && !isArray(obj)) { return '"' + obj.toString() + '"'; } else if (isObject(obj)) { ret += '{'; let keys = Object.keys(obj); for (let i = 0; i < keys.length; i++) { let key = keys[i]; ret += '"' + key.toString() + '":'; if (!isObject(obj[key]) && !isArray(obj[key])) { ret += '"' + obj[key].toString() + '"'; } else { ret += stringify(obj[key]); } if (i != keys.length - 1) { ret += ','; } } ret += '}'; } else if (isArray(obj)) { ret += '['; for (let i = 0; i < obj.length; i++) { ret += stringify(obj[i]); if (i != obj.length - 1) { ret += ','; } } ret += ']'; } return ret; } |
代码的思路就是传入一个对象,然后判断它的类型:1. 如果非数组和对象类型,就直接返回它的字符串形式;2. 如果是对象类型,则遍历每个键值对,判断每个键对应值的类型,如果是非字符串,则递归对它调用函数本身,否则直接在结果字符串中拼接他的字符串形式;3. 数组类型类似对象类型处理。这也是自己在面试中完成的版本。
二、类型问题
面试官又询问了是否可以保存 function 这种类型,其实这就牵扯到 JSON 这种数据格式所拥有的数据类型问题,JSON 是没有 function 这种数据类型的。其实可以想一下,JSON 的的目的是为了方便数据传输,而且其它语言也都有很多系统提供的库来处理 JSON,但其它语言并没有必要接收 function 这种类型,因为并没有办法直接执行,存储它也没有太大意义。
于是查找 JSON 的官方定义或者这篇文章,可以看到 JSON 的数据格式包括:
- Number
- String
- Boolean
- value
- Array
- Object
- Whitespace
- null
所以上面的代码是没有针对 Number / Boolean / null 这3种数据类型做处理的。针对这三种类型(其中 Boolean 需要判断true
和false
两种情况),只要对他们进行toString()
处理,而不要在外面包裹双引号即可。最直接的处理方法:使用条件分支判断数据类型,代码实现为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
function isArray () {} function isObject () {} function isString () {} function isNull () {} function processValue (obj) { if (!isString(obj)) { if (!isNull(obj)) { return obj.toString(); } else { return 'null'; } } else { return '"' + obj.toString() + '"'; } } function stringify (obj) { let ret = ''; if (!isObject(obj) && !isArray(obj)) { return processValue(obj); } else if (isObject(obj)) { ret += '{'; let keys = Object.keys(obj); for (let i = 0; i < keys.length; i++) { let key = keys[i]; ret += '"' + key.toString() + '":'; if (!isObject(obj[key]) && !isArray(obj[key])) { ret += processValue(obj[key]); } else { ret += stringify(obj[key]); } if (i != keys.length - 1) { ret += ','; } } ret += '}'; } else if (isArray(obj)) { ret += '['; for (let i = 0; i < obj.length; i++) { ret += stringify(obj[i]); if (i != obj.length - 1) { ret += ','; } } ret += ']'; } return ret; } let json = {'outter': {'inner': ["1", 2, {'item': 'content', 'null': null}]}}; let result = stringify(json); console.log(result); console.log(JSON.parse(result)); |
对于 Number / Boolean 可以直接使用toString()
,对于null
则需要直接返回'null'
字符串。以上就是所有实现,但是类型判断处代码使用了较深的if
分支判断,可以继续优化,而且没有考虑数据合法性校验的问题,待更新。另注意,JSON 中是没有undefined
这种数据类型的。
注:转载注明出处并联系作者,本文链接:https://nodefe.com/implementation-of-stringify/