转载

stringify 实现及 JSON 数据类型思考

背景

记前两天遇见的一道面试题,让现场写出 JavaScript 中 stringify 函数的实现。首先写一下自己最开始的思路,然后针对里面的一些问题进行逐步修改,并且引出对 JSON 这种轻量级数据传输格式所拥有数据类型的学习与思考。

一、最初实现

首先给定题目

将一个 JSON 格式对象转换为字符串,转换后的结果可以通过 JSON.parse() 方法将该字符串重新转换为一个 JSON 对象。

先简化问题,将数据类型简单划分(并不正确,下文会给描述)为:对象(Object)、数组(Array)和字符串(String)这三种类型。对于 String,只需要简单的对它进行 toString() 调用,并包裹在 " 中处理,对于前两者则需要采取不同操作。考虑到他们的子元素也是 JSON 对象,定义具有递归性,所以代码也采用递归来实现。最初代码如下:

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; }
function isArray () {} function isObject () {}   function stringify (obj) {   letret = '';      if (!isObject(obj) && !isArray(obj)) {     return '"' + obj.toString() + '"';   } else if (isObject(obj)) {     ret += '{';     letkeys = Object.keys(obj);     for (let i = 0; i < keys.length; i++) {       letkey = 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 的数据格式包括:

  1. Number
  2. String
  3. Boolean
  4. value
  5. Array
  6. Object
  7. Whitespace
  8. null

所以上面的代码是没有针对 Number / Boolean / null 这3种数据类型做处理的。针对这三种类型(其中 Boolean 需要判断 truefalse 两种情况),只要对他们进行 toString() 处理,而不要在外面包裹双引号即可。最直接的处理方法:使用条件分支判断数据类型,代码实现为:

function isArray () {} function isObject () {} function isString () {} function isNull () {}  function stringify (obj) {   let ret = '';    if (!isObject(obj) && !isArray(obj)) {     if (!isString(obj)) {       if (!isNull(obj)) {         return obj.toString();       } else {         return 'null';       }        } else {       return '"' + obj.toString() + '"';     }      } else if (isObject(obj)) {     ret += '{';     let keys = Object.keys(obj);     for (let i = 0; i &lt; keys.length; i++) {       let key = keys[i];       ret += '"' + key.toString() + '":';       if (!isObject(obj[key]) && !isArray(obj[key])) {         if (!isString(obj[key])) {           if (!isNull(obj[key])) {             ret += obj[key].toString();           } else {             ret += 'null';           }         } else {           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 &lt; 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));
function isArray () {} function isObject () {} function isString () {} function isNull () {}   function stringify (obj) {   letret = '';     if (!isObject(obj) && !isArray(obj)) {     if (!isString(obj)) {       if (!isNull(obj)) {         return obj.toString();       } else {         return 'null';       }       } else {       return '"' + obj.toString() + '"';     }     } else if (isObject(obj)) {     ret += '{';     letkeys = Object.keys(obj);     for (let i = 0; i < keys.length; i++) {       letkey = keys[i];       ret += '"' + key.toString() + '":';       if (!isObject(obj[key]) && !isArray(obj[key])) {         if (!isString(obj[key])) {           if (!isNull(obj[key])) {             ret += obj[key].toString();           } else {             ret += 'null';           }         } else {           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; }   letjson = {'outter': {'inner': ["1", 2, {'item': 'content', 'null': null}]}};   letresult = stringify(json);   console.log(result);   console.log(JSON.parse(result)); 

对于 Number / Boolean 可以直接使用 toString() ,对于 null 则需要直接返回 'null' 字符串。以上就是所有实现,但是类型判断处代码使用了较深的 if 分支判断,可以继续优化,而且没有考虑数据合法性校验的问题,待更新。另注意,JSON 中是没有 undefined 这种数据类型的。

原文  http://nodefe.com/implementation-of-stringify/
正文到此结束
Loading...