手写一个JSON.stringify

481 阅读3分钟

分类处理

JS 基本数据类型 null undefined number string boolean(ES5) Bigint Symbol(ES6)

JS 复杂数据类型 object date array function regExp...

基本上都是转字符串, 字符串本身再转, Bigint不能转会爆类型错误, Symbol, function转undefined(在数组里会被转成null,在对象内会被忽略), regExp转{}, date的话通过date.toJSON()再转字符串

代码实现

主函数

// JSON.Stringify
const jsonStringify = function(data) {
 return handleType(data) === 'object' ? handleJsonStringifyIsObject(data) : handleJsonStringifyNotObject(data);
}

判断类型

const handleType = function(target) {
  return typeof target;
}

处理typeof!=='object'

// 情况1 : 处理typeof检测的非对象 PS NaN/Infinity类型为number // null undefined boolean string number BigInt
const handleJsonStringifyNotObject = function(data) {
  if(Number.isNaN(data) || data === Infinity) return "null";
  if(handleType(data) === 'undefined' || handleType(data) === 'symbol') return undefined;
  if(handleType(data) === 'string') return `'${data}'`
  if(handleType(data) === 'boolean' || handleType(data) === 'number') return `${data}`
  if(data instanceof Function) return undefined;
  if(handleType(data) === 'bigint') throw new TypeError('Do not know how to serialize a BigInt');
  return data;
}

处理typeof==='object'

// 情况2 : 处理对象类型 大致为 null/regExp/array/object PS:历史原因null为object 数组和对象需要遍历执行
const handleJsonStringifyIsObject = function(data) {
  if(data === null) return 'null';
  if(data.toJSON && typeof data.toJSON === 'function') return jsonStringify(data.toJSON()); // 处理 Date 的序列化
  if(data instanceof RegExp) return "{}";
  if(data instanceof Array) return handleJsonStringifyArray(data);
  return handleJsonStringifyObject(data);
}

遍历数组

// 情况2 下的情况小1: 遍历对象进行序列化 
const handleJsonStringifyObject = function(data) {
  let result = [];
  Object.keys(data).forEach((item, index)=>{
    if(handleType(item) === 'symbol' || handleType(data[item]) === 'symbol' || data[item] === undefined || handleType(data[item]) === 'function') {
    }
    else
      result.push(`"${item}":${jsonStringify(data[item])}`);
  })
  return `{${result}}`.replace(/'/g, '"');
}

遍历对象

// 情况2 下的情况小2: 遍历数组进行序列化 
const handleJsonStringifyArray = function(data) {
  let result = [];
  data.forEach((item, index)=>{
    if(item === 'undefined' || handleType(item) === 'function' || handleType(item) === 'symbol' || item === undefined) result[index] = "null";
    else result[index] = jsonStringify(item);
  })
  return `[${result}]`.replace(/'/g, '"');
}

完整代码

const handleType = function(target) {
  return typeof target;
}
// 情况2 下的情况小2: 遍历数组进行序列化 
const handleJsonStringifyArray = function(data) {
  let result = [];
  data.forEach((item, index)=>{
    if(item === 'undefined' || handleType(item) === 'function' || handleType(item) === 'symbol' || item === undefined) result[index] = "null";
    else result[index] = jsonStringify(item);
  })
  return `[${result}]`.replace(/'/g, '"');
}
// 情况2 下的情况小1: 遍历对象进行序列化 
const handleJsonStringifyObject = function(data) {
  let result = [];
  Object.keys(data).forEach((item, index)=>{
    if(handleType(item) === 'symbol' || handleType(data[item]) === 'symbol' || data[item] === undefined || handleType(data[item]) === 'function') {
    }
    else
      result.push(`"${item}":${jsonStringify(data[item])}`);
  })
  return `{${result}}`.replace(/'/g, '"');
}
// 情况2 : 处理对象类型 大致为 null/regExp/array/object PS:历史原因null为object 数组和对象需要遍历执行
const handleJsonStringifyIsObject = function(data) {
  if(data === null) return 'null';
  if(data.toJSON && typeof data.toJSON === 'function') return jsonStringify(data.toJSON()); // 处理 Date 的序列化
  if(data instanceof RegExp) return "{}";
  if(data instanceof Array) return handleJsonStringifyArray(data);
  return handleJsonStringifyObject(data);
}
// 情况1 : 处理typeof检测的非对象 PS NaN/Infinity类型为number // null undefined boolean string number BigInt
const handleJsonStringifyNotObject = function(data) {
  if(Number.isNaN(data) || data === Infinity) return "null";
  if(handleType(data) === 'undefined' || handleType(data) === 'symbol') return undefined;
  if(handleType(data) === 'string') return `'${data}'`
  if(handleType(data) === 'boolean' || handleType(data) === 'number') return `${data}`
  if(data instanceof Function) return undefined;
  if(handleType(data) === 'bigint') throw new TypeError('Do not know how to serialize a BigInt');
  return data;
}
// JSON.Stringify
const jsonStringify = function(data) {
 return handleType(data) === 'object' ? handleJsonStringifyIsObject(data) : handleJsonStringifyNotObject(data);
}
// TODO Test 
let obj = {
  name: 'sina', // string
  age:  21,  // number
  love: Infinity, // infinity
  ability: NaN, //NaN
  brith: new Date(), // date
  address: { // object
    'country': 'CN',
    'city': 'NC'
  },
  sex: true,
  // only: Symbol(1),
  regexp: new RegExp(/\s+/g),
  // 以下都未处理
  function: function add(a,b) {return a+b;},
  undefine: undefined,
  symbol: Symbol(1),
}
// function->undefined
function add(a,b) {return a+b;};
let arr = [add, Symbol(1), undefined];
// array->array
console.log(jsonStringify(arr));
console.log(JSON.stringify(arr));
// function->undefined
console.log(jsonStringify(add));
console.log(JSON.stringify(add));
// boolean->'boolean'
console.log(jsonStringify(true));
console.log(JSON.stringify(true));
// number->'number'
console.log(jsonStringify(12));
console.log(JSON.stringify(12));
// null->'null'
console.log(jsonStringify(null));
console.log(JSON.stringify(null));
// undefined->undefined
console.log(jsonStringify(undefined));
console.log(JSON.stringify(undefined));
// string->"'string'"
console.log(jsonStringify('undefined'));
console.log(JSON.stringify('undefined'));
// symbol->undefined
console.log(jsonStringify(Symbol(1)));
console.log(JSON.stringify(Symbol(1)));
// array->'array'
console.log(jsonStringify([1,2,3]));
console.log(JSON.stringify([1,2,3]));
// date->'array'
console.log(jsonStringify(new Date()));
console.log(JSON.stringify(new Date()));
// obj->"'obj'"
console.log(jsonStringify(obj));
console.log(JSON.stringify(obj));
// regExp->'{}'
console.log(jsonStringify(new RegExp()));
console.log(JSON.stringify(new RegExp()));

// Bigint->'Uncaught TypeError: Do not know how to serialize a BigInt '
console.log(jsonStringify(BigInt(1)));
console.log(JSON.stringify(BigInt(1)));

总结

基本上都是转字符串, 字符串本身再转, Bigint不能转会爆类型错误, Symbol, function转undefined(在数组里会被转成null,在对象内会被忽略), regExp转{}, date的话通过date.toJSON()再转字符串