网易首页 > 网易号 > 正文 申请入驻

前端进阶: 如何用 Javascript 存储函数?

0
分享至

作者: 徐小夕 来源: 趣谈前端

任何一家Saas企业都需要有自己的低代码平台.在可视化低代码的前端研发过程中, 发现了很多有意思的技术需求, 在解决这些需求的过程中, 往往也会给自己带来很多收获, 今天就来分享一下在研发Dooring过程中遇到的前端技术问题——javascript函数存储.

背景介绍

我们都知道要想搭建一个前端页面基本需要如下3个要素:

  • 元素(UI)
  • 数据(Data)
  • 事件/交互(Event)

在 数据驱动视图 的时代, 这三个要素的关系往往如下图所示:

可视化搭建平台的设计思路往往也是基于上面的过程展开的, 我们需要提供编辑器环境给用户来创建视图和交互, 最终用户保存的产物可能是这样的:

{
"name": "Dooring表单",
"bgColor": "#666",
"share_url": "http://xxx.cn",
"mount_event": [
{
"id": "123",
"func": () => {
// 初始化逻辑
GamepadHapticActuator();
},
"sourcedata": []
}
],
"body": [
{
"name": "header",
"event": [
{
"id": "123",
"type": "click",
"func": () => {
// 组件自定义交互逻辑
showModal();
}
}
]
}
]
}

那么问题来了, json 字符串我们好保存(可以通过JSON.stringify序列化的方式), 但是如何将函数也一起保存呢? 保存好了函数如何在页面渲染的时候能正常让 js 运行这个函数呢?

实现方案思考

趣谈前端

我们都知道将 js 对象转化为json 可以用 JSON.stringify 来实现, 但是它也会有局限性, 比如:

  • 转换值如果有 toJSON() 方法,那么由 toJson() 定义什么值将被序列化
  • 非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中
  • 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值
  • undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined,如JSON.stringify(function(){}) or JSON.stringify(undefined)
  • 所有以 symbol 属性键的属性都会被完全忽略掉,即便 replacer 参数中强制指定包含了它们
  • Date 日期调用了 toJSON() 将其转换为了 string 字符串(同Date.toISOString()),因此会被当做字符串处理
  • NaN 和 Infinity 格式的数值及 null 都会被当做 null
  • 其他类型的对象,包括 Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性

我们可以看到第4条, 如果我们序列化的对象中有函数, 它将会被忽略! 所以常理上我们使用JSON.stringify 是无法保存函数的, 那还有其他办法吗?

也许大家会想到先将函数转换成字符串, 再用 JSON.stringify 序列化后保存到后端, 最后在组件使用的时候再用 eval 或者 Function 将字符串转换成函数. 大致流程如下:

不错, 理想很美好, 但是现实很_______.

接下来我们就一起分析一下关键环节 func2string 和 string2func 如何实现的.

js存储函数方案设计

熟悉 JSON API 的朋友可能会知道 JSON.stringify 支持3个参数, 第二个参数 replacer 可以是一个函数或者一个数组。作为函数,它有两个参数,键(key)和值(value),它们都会被序列化。 函数需要返回 JSON 字符串中的 value, 如下所示:

如果返回一个 Number, 转换成相应的字符串作为属性值被添加入 JSON 字符串

如果返回一个 String, 该字符串作为属性值被添加入 JSON 字符串

如果返回一个 Boolean, 则 "true" 或者 "false" 作为属性值被添加入 JSON 字符串

如果返回任何其他对象,该对象递归序列化成 JSON 字符串,对每个属性调用 replacer 方法。除非该对象是一个函数,这种情况将不会被序列化成 JSON 字符

如果返回 undefined,该属性值不会在 JSON 字符串中输出

所以我们可以在第二个函数参数里对 value类型为函数的数据进行转换。如下:

const stringify = (obj) => {
return JSON.stringify(obj, (k, v) => {
if(typeof v === 'function') {
return `${v}`
}
return v
})
}

这样我们看似就能把函数保存到后端了. 接下来我们看看如何反序列化带函数字符串的 json.

因为我们将函数转换为字符串了, 我们在反解析时就需要知道哪些字符串是需要转换成函数的, 如果不对函数做任何处理我们可能需要人肉识别.

人肉识别的缺点在于我们需要用正则把具有函数特征的字符串提取出来, 但是函数写法有很多, 我们要考虑很多情况, 也不能保证具有函数特征的字符串一定是函数.

所以我换了一种简单的方式, 可以不用写复杂正则就能将函数提取出来, 方法就是在函数序列化的时候注入标识符, 这样我们就能知道那些字符串是需要解析为函数了, 如下:

stringify: function(obj: any, space: number | string, error: (err: Error | unknown) => {}) {
try {
return JSON.stringify(obj, (k, v) => {
if(typeof v === 'function') {
return `${this.FUNC_PREFIX}${v}`
}
return v
}, space)
} catch(err) {
error && error(err)
}
}

this.FUNC_PREFIX 就是我们定义的标识符, 这样我们在用 JSON.parse 的时候就能快速解析函数了. JSON.parse 也支持第二个参数, 他的用法和 JSON.stringify 的第二个参数类似, 我们可以对它进行转换, 如下:

parse: function(jsonStr: string, error: (err: Error | unknown) => {}) {
try {
return JSON.parse(jsonStr, (key, value) => {
if(value && typeof value === 'string') {
return value.indexOf(this.FUNC_PREFIX) > -1 ? new Function(`return ${value.replace(this.FUNC_PREFIX, '')}`)() : value
}
return value
})
} catch(err) {
error && error(err)
}
}

new Function 可以把字符串转换成 js 函数, 它只接受字符串参数,其可选参数为方法的入参,必填参数为方法体内容, 一个形象的例子:

我们上述的代码中函数体的内容:

new Function(`return ${value.replace(this.FUNC_PREFIX, '')}`)()

之所以要 return 是为了把原函数原封不动的还原, 大家也可以用 eval , 但是出于舆论还是谨慎使用.

以上方案已经能实现前端存储函数的功能了, 但是为了更工程化和健壮性还需要做很多额外的处理和优化, 这样才能让更多人开箱即用的使用你的库.

最后

为了让更多人能直接使用这个功能, 我将完整版 json 序列化方案封装成了类库, 支持功能如下:

stringify 在原生JSON.stringify 的基础上支持序列化函数,错误回调

parse 在原生JSON.parse 的基础上支持反序列化函数,错误回调

funcParse 将js对象中的函数一键序列化, 并保持js对象类型不变

安装方式如下:

# or npm install xijs yarn add xijs

使用:

import { parser } from 'xijs';
const a = {
x: 12,
b: function() {
alert(1)
}
}
const json = parser.stringify(a);
const obj = parser.parse(json);
// 调用方法
obj.b();

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
BBA中国定价权崩了:宝马7系降27万没人要,豪华车市场彻底变天

BBA中国定价权崩了:宝马7系降27万没人要,豪华车市场彻底变天

捣蛋窝
2026-05-24 14:46:47
正当防卫被判死刑,枪决前6分钟被最高法紧急叫停,董伟案始末

正当防卫被判死刑,枪决前6分钟被最高法紧急叫停,董伟案始末

易玄
2026-05-25 01:45:09
雪上加霜!上海硬刚撕下漳州杨梅“遮羞布”,喊话漳州引刀自宫

雪上加霜!上海硬刚撕下漳州杨梅“遮羞布”,喊话漳州引刀自宫

娱乐圈见解说
2026-05-25 02:00:32
人生如戏全靠演技!张雪机车“控分”阳谋,把WSBK规则玩明白了

人生如戏全靠演技!张雪机车“控分”阳谋,把WSBK规则玩明白了

长江浊酒客
2026-05-24 17:30:03
终于撑不住了!特朗普重大让步!美伊迎来惊天逆转!

终于撑不住了!特朗普重大让步!美伊迎来惊天逆转!

大嘴说天下
2026-05-24 21:17:22
活塞官方祝贺杜伦入最佳3阵!美记直言最烂投票 能签2.87亿顶薪?

活塞官方祝贺杜伦入最佳3阵!美记直言最烂投票 能签2.87亿顶薪?

颜小白的篮球梦
2026-05-25 08:37:48
尘埃落定!决赛2-3输球仅1天,日籍主帅下课悬念揭晓,董路怒批!

尘埃落定!决赛2-3输球仅1天,日籍主帅下课悬念揭晓,董路怒批!

大秦壁虎白话体育
2026-05-24 08:54:31
“全家福”来了!中国航天员完成第8次“太空会师”

“全家福”来了!中国航天员完成第8次“太空会师”

扬子晚报
2026-05-25 07:16:11
孩子被开水烫伤,妈妈直接用冰块敷坑了娃,医生:组织坏死毁容了

孩子被开水烫伤,妈妈直接用冰块敷坑了娃,医生:组织坏死毁容了

菁妈育儿
2026-05-24 10:36:51
深圳一张征婚表火了!42岁女士要求男方净资产2000万,遭全网骂惨

深圳一张征婚表火了!42岁女士要求男方净资产2000万,遭全网骂惨

谭谈社会
2026-05-23 11:29:48
53岁歌手自曝多妻生活:妻子们不能找别人,必须尊重大房

53岁歌手自曝多妻生活:妻子们不能找别人,必须尊重大房

生活观察员啊
2026-05-24 01:16:18
中纪委网站:汪斌被开除党籍!

中纪委网站:汪斌被开除党籍!

双一流高校
2026-05-25 00:10:59
荒唐至极!欧盟外长声称:中国让欧洲得了癌症,5个国家当场抗议

荒唐至极!欧盟外长声称:中国让欧洲得了癌症,5个国家当场抗议

浪子阿邴聊体育
2026-05-24 07:29:59
3-2爆冷!黄博文率领西海岸队狂飙:4轮拿8分,完胜郑智

3-2爆冷!黄博文率领西海岸队狂飙:4轮拿8分,完胜郑智

何老师呀
2026-05-24 21:20:26
山西矿难已致90死!国务院“较真碰硬”背后,老板会被判重刑吗?

山西矿难已致90死!国务院“较真碰硬”背后,老板会被判重刑吗?

荆门热点
2026-05-24 09:32:09
许家印英国18亿豪宅被流浪汉占领3年,门廊养鲜花,邻里主动接济

许家印英国18亿豪宅被流浪汉占领3年,门廊养鲜花,邻里主动接济

译言
2026-05-23 04:19:29
女婿正国级,儿子副国级,这位厉害母亲给中国留下最宝贵遗产!

女婿正国级,儿子副国级,这位厉害母亲给中国留下最宝贵遗产!

近史谈
2026-05-24 01:00:15
唐斯发牌,大头逆袭:骑士跌向残忍夏季

唐斯发牌,大头逆袭:骑士跌向残忍夏季

张佳玮写字的地方
2026-05-24 11:41:26
白酒一哥也扛不住了……

白酒一哥也扛不住了……

Mask的小酒馆
2026-05-25 07:35:57
33岁战神泪流满面!6万人高唱你永不独行 自宣下一站:遥远的地方

33岁战神泪流满面!6万人高唱你永不独行 自宣下一站:遥远的地方

风过乡
2026-05-25 07:38:15
2026-05-25 09:07:00
Nodejs开发
Nodejs开发
分享只有程序员懂的干货
648文章数 823关注度
往期回顾 全部

科技要闻

神舟二十三号航天员乘组顺利进驻“天宫”

头条要闻

男子吃几口鱼子后痛到崩溃 医生:别拿命赌 没特效解药

头条要闻

男子吃几口鱼子后痛到崩溃 医生:别拿命赌 没特效解药

体育要闻

唐斯发牌,大头逆袭:骑士跌向残忍夏季

娱乐要闻

王鹤棣掉粉超20万!代言和作品遭抵制

财经要闻

退市!33年“A股不死鸟”落幕

汽车要闻

国民家轿再上新 帝豪向上系列限时5.59万起

态度原创

教育
游戏
数码
公开课
军事航空

教育要闻

学校心理健康教育支持体系构建的多维路径探索

《刺客信条:黑旗》重制版有新终局章节 结局或有改动

数码要闻

惠普发布ZBook 8 G2a笔记本,最高配超5万

公开课

李玫瑾:为什么性格比能力更重要?

军事要闻

俄军出动“榛树”导弹袭击乌克兰

无障碍浏览 进入关怀版