木灵鱼儿
阅读:2655
JSON 格式化对象,对象中存在函数的解决办法,JSON格式化函数
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(){})
orJSON.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(); //我也爱你!
至此,对象转字符加函数也转换的方法完成。
版权申明
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿 - 有梦就能远航站点。未经许可,禁止转载。
相关推荐
第四章 对象的创建
命名空间在ESM模块化还未出来之前,用于减少全局变量名污染的一种做法就是使用命名空间,其做法也非常简单,就是创建一个全局的变量,然后将内容都赋值给这个变量,从而减少对全局变量的使用。//创建命名空间 var MYAPP = {}; //构造函数 MYAPP.Parent = function(){}; MYAPP.Child = function(){}; // 一个变量 MYAPP.some_var =1; // 一个对象容器 MYAPP.modules ={}; // 嵌套的对象 MYAPP.modules.module1 ={}; MYAPP.modules.module1...
第三章 函数
背景简介JavaScript中函数有两个主要特点使其变得特殊:函数是“第一类对象”,更久之前也称之为“第一等公民”函数可以提供作用域什么是第一类对象第一类对象(First-class object)这个名称可以追溯到1960年,原称为第一类公民(First-class citizen),简单点来总结来说,可以在该语言中做到其他元素都能进行的所有操作(你们能干的我也都能干),就可以称为一等公民。一等公民的概念在《计算机程序的构造和解释》书籍中提及,而我们JavaScript的一等公民的概念是因为Brendan Eich(布兰登·艾奇 - JS之父)在设计语言是借鉴了Scheme语言,在Sc...
利用JSON过滤对象和数组中指定的key属性
有时候我们在vue中进行for循环,就会涉及到绑定唯一值key的问题,但是并不是任何时候都会存在所谓的唯一值,使用index下标明显是不合适的,官方也不推荐,除非你for循环出来的列表不用变化。所以一般常用的做法就是给for循环的对象添加一个属性,属性的值是随机的uuid或者时间戳。这样前端问题解决了,如果遍历的数据还需要提交到后端,那么不就多了一个属性,这个属性后端不需要的。所以,我们需要在提交数据前,对数据进行过滤。过滤又得for循环删除?那怎么行,有没有那种通用的,简单的方法。过滤方法/** * @description: 过滤对象中指定的属性,也可以拿来浅拷贝 * @para...
对象扁平化
前言后端返回给前端的数据,有时候会是一个多层级对象,但是我们前端使用的时候,for循环遍历渲染时,多层级对象往往需要进行单独处理,因为还需要判断这个key值是否存在,否则会报错。强行让后端改变数据结构又好像不现实,无奈,只有自己处理了。掘金看到一位大佬文章《【算法】JS 实现对象的扁平化》感觉很合适,逻辑清晰。要求将对象中的层级扁平化,改成如下格式:// 实现一个 flatten 函数,实现如下的转换功能 const obj = { a: 1, b: [1, 2, { c: true }], c: { e: 2, f: 3 }, g: null, }; // 转换为 l...
fetch 请求报错处理思路
之前写过一片使用fetch下载文件的方法,但是如果后端返回一个错误对象,使用response.blob()会将这个错误对象直接转为blob对象,然后被转为文件下载下来。但实际上我们是要区分处理的,比如是文件流我们就下载文件,是错误对象,我们就进行错误提示和抛出错误。所以这里,我主要分享下我对错误处理的思路。基础知识首先,在fetch第一个then回调中,我们可以对response对象进行三种方法操作:response.text 返回的是一个纯文本 是一个promise对象response.json 返回的是一个对象(json/array) 是一个promise对象response.b...

Object 对象的扩展
属性的简写es6允许对属性进行简写,可以直接使用变量,变量名直接成为了属性名。var a = "hello"; var b = {a}; //等同于 var b = { a:a }属性里的方法也可以简写var a = { b(){...} } //等同于 var a = { b: function(){ ... } }在CommonJS模块输出变量时,也就是node模块导出时,这种简写就显得十分方便,我们直接导致一个对象,对象里面使用简写。var a = {}; var b = {}; module.exports = {a,b}而属性的赋...
H-Viewer e绅士新规则
采用之前大佬在github上分享的规则,我修复了tga标签和预览图的bug,目前使用海阔以,特此分享!规则:{"categories":[ { "cid":1, "title":"首页", "url":"https://exhentai.org/?page\u003d{page:0}" },{ "cid":2, "title":...
Ajax 表单序列化
什么是表单序列化呢?将所有表单的提交通过一个标准化的方法去获取并且提交出去,那就是序列化,也就是说不同的表单,如注册啊,登录啊,修改资料啊,这些东西可以通过一个通用的方法去处理它。那么表单序列化有几个要求:不能发送禁用的表单字段;只发送勾选的复选框和单选按钮;不发送type是reset、submit、file、button以及字段集;多选选择框中的每个选中的值单独一个条目;对于select元素,如果有value值,就指定value作为发送的字段,如果没有,就指定text值;已经将ajax的代码作为单独的一个文件保存,而调用则使用ajax()的方法,之前也做了一个表单提交的方法,在所有条件...
自定义insertAfter() 在当前元素节点的后面添加新的内容
由于document中并没有insertAfter()这个功能,只有insertBefore()在当前元素节点的前面添加新的内容,在后面添加是没有的,但是我们可以自己做一个有这个功能得函数。首先有几点前提条件:该元素节点后面有其他同级得节点如果没有同级得节点他应该就是最后一个节点由于我们写html时会有换行得操作,导致产生了空白节点,所以我们还需要移除这个空白节点才能正常操作。我们先写一个简单得html代码:<body> /*这是该元素为最后一个节点*/ <div id="box"> <p>1</p> <...