json在格式化对象是,如果是普通的键值对对象还是没有问题的,但是如果他的值是一个函数,在格式化之后,函数就会被剔除。

const a = {
  name: '我爱你',
  fn: function(){
    console.log("我也爱你!")
  }
};

const b = JSON.stringify(a);

当你输出变量b的时候,你得到的会是:

"{\"name\":\"我爱你\"}"

fn不见了!

引用JSON.stringify()方法描述中的一段解释:

undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined,如JSON.stringify(function(){}) or JSON.stringify(undefined)

可以知道,在非数组对象中的函数,会在转换过程中被忽略。这也就是导致fn消失的原因。

函数格式化方法

我们可以使用stringify的第二个参数,这个参数可以是一个函数,也可以是一个数组,官方的示意如下:

如果该参数是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;如果该参数为 null 或者未提供,则对象所有的属性都会被序列化。

传入函数时,我们可以通过这个函数或者每次被转换的key和val值。

function对象本身存在一个toString方法,toString() 方法返回一个表示当前函数源代码的字符串。

function sum(a, b) {
  return a + b;
}
console.log(sum.toString());
//得到字符串格式: 'function sum(a, b) { return a + b; }'

那么这就够了。

const a = {
  name: '我爱你',
  fn: function(){
    console.log("我也爱你!")
  }
};

const b = JSON.stringify(a,function(key, val) {
  if (typeof val === 'function') {
  return val.toString();
  }
  return val;
});

console.log(b);  
//{"name":"我爱你","fn":"function() {\n console.log(\"我也爱你!\")\n }"}

函数格式化完成。

函数格式化还原方法

函数格式化后,必然要还原使用,毕竟不可能永远是格式化,没有解析使用的。

但是字符串的解析,自然而然要用到字符转代码,绕不过去一个功能就是eval,虽然可以用Function的特性,不使用eval,但是,终究,在string转成代码时,会触发运行,比如alert()这些,就会被触发,这也导致了一个安全性的问题,你不知道你将要转化的内容,是否安全。

所以,这也是这些年来,为什么不推荐使用eval的原因。

怎么办?

目前es6的出现,可以很好的解决这个问题,我们可以自己建立一个沙箱机制,控制被转化的代码,运行时,只能获取我们允许的内容,不允许的,将禁止。

我们可以写一个粗浅的沙箱方法,将代码放置在沙箱中运行。

function sandBox(value) {
      const withStr = `with(obj) { return ${ value } }`;
      //创建监听对象
      const proxy = new Proxy(Object.create(null), {
        has(target, key) {
          if (['console', 'Math', 'Date'].includes(key)) {
            return target[key];
          }
          return true;
        },
        get(target, key) {
          if (key === Symbol.unscopables) return undefined;
          return target[key]
        }
      });
      return new Function('obj', withStr)(proxy); //将监听的对象作为obj参数传入
    };

创建一个没有原型的object对象也是一个无奈之举,为了安全,甚至不建议在这个obj对象里面添加属性,因为哪怕是基本的属性,string值这些,以及是有原型的,通过原型链,对方以及可以获取到你最外层的对象,做一些破坏性的事情。

这个沙箱不能说是绝对安全,但是目前来说,没有什么问题,以后有机会再折腾一下iframe沙箱方法吧。

在得到沙箱的方法后,我们解析就可以这样写:

const c = JSON.parse(b, function(key, val) {
  if (/^function\s*\(.*\)\s*{/.test(val) || /^\(.*\)\s*=>/.test(val) || /^.*\s*\(.*\)\s*{/.test(val)) {
    return sandBox(val);
  }
  return val;
});

c.fn(); //我也爱你!

至此,对象转字符加函数也转换的方法完成。

分类: JavaScript 标签: 对象函数json沙箱

评论

暂无评论数据

暂无评论数据

目录