自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

前端進(jìn)階: 如何用 Javascript 存儲(chǔ)函數(shù)?

開發(fā) 前端 存儲(chǔ)設(shè)備
今天就來分享一下在研發(fā)Dooring過程中遇到的前端技術(shù)問題——javascript函數(shù)存儲(chǔ).

[[437221]]

任何一家Saas企業(yè)都需要有自己的低代碼平臺(tái).在可視化低代碼的前端研發(fā)過程中, 發(fā)現(xiàn)了很多有意思的技術(shù)需求, 在解決這些需求的過程中, 往往也會(huì)給自己帶來很多收獲, 今天就來分享一下在研發(fā)Dooring過程中遇到的前端技術(shù)問題——javascript函數(shù)存儲(chǔ).

背景介紹

我們都知道要想搭建一個(gè)前端頁面基本需要如下3個(gè)要素:

  • 元素(UI)
  • 數(shù)據(jù)(Data)
  • 事件/交互(Event)

在 數(shù)據(jù)驅(qū)動(dòng)視圖 的時(shí)代, 這三個(gè)要素的關(guān)系往往如下圖所示:

趣談前端

可視化搭建平臺(tái)的設(shè)計(jì)思路往往也是基于上面的過程展開的, 我們需要提供編輯器環(huán)境給用戶來創(chuàng)建視圖和交互, 最終用戶保存的產(chǎn)物可能是這樣的:

  1.     "name""Dooring表單"
  2.     "bgColor""#666"
  3.     "share_url""http://xxx.cn"
  4.     "mount_event": [ 
  5.         { 
  6.             "id""123"
  7.             "func": () => { 
  8.                 // 初始化邏輯 
  9.                 GamepadHapticActuator(); 
  10.             }, 
  11.             "sourcedata": [] 
  12.         } 
  13.     ], 
  14.     "body": [ 
  15.         { 
  16.             "name""header"
  17.             "event": [ 
  18.                 { 
  19.                     "id""123"
  20.                     "type""click"
  21.                     "func": () => { 
  22.                         // 組件自定義交互邏輯 
  23.                         showModal(); 
  24.                     } 
  25.                 } 
  26.             ] 
  27.         } 
  28.     ] 

那么問題來了, json 字符串我們好保存(可以通過JSON.stringify序列化的方式), 但是如何將函數(shù)也一起保存呢? 保存好了函數(shù)如何在頁面渲染的時(shí)候能正常讓 js 運(yùn)行這個(gè)函數(shù)呢?

實(shí)現(xiàn)方案思考

趣談前端

我們都知道將 js 對(duì)象轉(zhuǎn)化為json 可以用 JSON.stringify 來實(shí)現(xiàn), 但是它也會(huì)有局限性, 比如:

  • 轉(zhuǎn)換值如果有 toJSON() 方法,那么由 toJson() 定義什么值將被序列化
  • 非數(shù)組對(duì)象的屬性不能保證以特定的順序出現(xiàn)在序列化后的字符串中
  • 布爾值、數(shù)字、字符串的包裝對(duì)象在序列化過程中會(huì)自動(dòng)轉(zhuǎn)換成對(duì)應(yīng)的原始值
  • undefined、任意的函數(shù)以及 symbol 值,在序列化過程中會(huì)被忽略(出現(xiàn)在非數(shù)組對(duì)象的屬性值中時(shí))或者被轉(zhuǎn)換成 null(出現(xiàn)在數(shù)組中時(shí))。函數(shù)、undefined 被單獨(dú)轉(zhuǎn)換時(shí),會(huì)返回 undefined,如JSON.stringify(function(){}) or JSON.stringify(undefined)
  • 所有以 symbol 為屬性鍵的屬性都會(huì)被完全忽略掉,即便 replacer 參數(shù)中強(qiáng)制指定包含了它們
  • Date 日期調(diào)用了 toJSON() 將其轉(zhuǎn)換為了 string 字符串(同Date.toISOString()),因此會(huì)被當(dāng)做字符串處理
  • NaN 和 Infinity 格式的數(shù)值及 null 都會(huì)被當(dāng)做 null
  • 其他類型的對(duì)象,包括 Map/Set/WeakMap/WeakSet,僅會(huì)序列化可枚舉的屬性

我們可以看到第4條, 如果我們序列化的對(duì)象中有函數(shù), 它將會(huì)被忽略! 所以常理上我們使用JSON.stringify 是無法保存函數(shù)的, 那還有其他辦法嗎?

也許大家會(huì)想到先將函數(shù)轉(zhuǎn)換成字符串, 再用 JSON.stringify 序列化后保存到后端, 最后在組件使用的時(shí)候再用 eval 或者 Function 將字符串轉(zhuǎn)換成函數(shù). 大致流程如下:

趣談前端

不錯(cuò), 理想很美好, 但是現(xiàn)實(shí)很_______.

接下來我們就一起分析一下關(guān)鍵環(huán)節(jié) func2string 和 string2func 如何實(shí)現(xiàn)的.

js存儲(chǔ)函數(shù)方案設(shè)計(jì)

熟悉 JSON API 的朋友可能會(huì)知道 JSON.stringify 支持3個(gè)參數(shù), 第二個(gè)參數(shù) replacer 可以是一個(gè)函數(shù)或者一個(gè)數(shù)組。作為函數(shù),它有兩個(gè)參數(shù),鍵(key)和值(value),它們都會(huì)被序列化。 函數(shù)需要返回 JSON 字符串中的 value, 如下所示:

如果返回一個(gè) Number, 轉(zhuǎn)換成相應(yīng)的字符串作為屬性值被添加入 JSON 字符串

如果返回一個(gè) String, 該字符串作為屬性值被添加入 JSON 字符串

如果返回一個(gè) Boolean, 則 "true" 或者 "false" 作為屬性值被添加入 JSON 字符串

如果返回任何其他對(duì)象,該對(duì)象遞歸地序列化成 JSON 字符串,對(duì)每個(gè)屬性調(diào)用 replacer 方法。除非該對(duì)象是一個(gè)函數(shù),這種情況將不會(huì)被序列化成 JSON 字符

如果返回 undefined,該屬性值不會(huì)在 JSON 字符串中輸出

所以我們可以在第二個(gè)函數(shù)參數(shù)里對(duì) value類型為函數(shù)的數(shù)據(jù)進(jìn)行轉(zhuǎn)換。如下:

  1. const stringify = (obj) => { 
  2.     return JSON.stringify(obj, (k, v) => { 
  3.       if(typeof v === 'function') { 
  4.           return `${v}` 
  5.       } 
  6.       return v 
  7.     }) 

這樣我們看似就能把函數(shù)保存到后端了. 接下來我們看看如何反序列化帶函數(shù)字符串的 json.

因?yàn)槲覀儗⒑瘮?shù)轉(zhuǎn)換為字符串了, 我們?cè)诜唇馕鰰r(shí)就需要知道哪些字符串是需要轉(zhuǎn)換成函數(shù)的, 如果不對(duì)函數(shù)做任何處理我們可能需要人肉識(shí)別.

人肉識(shí)別的缺點(diǎn)在于我們需要用正則把具有函數(shù)特征的字符串提取出來, 但是函數(shù)寫法有很多, 我們要考慮很多情況, 也不能保證具有函數(shù)特征的字符串一定是函數(shù).

所以我換了一種簡單的方式, 可以不用寫復(fù)雜正則就能將函數(shù)提取出來, 方法就是在函數(shù)序列化的時(shí)候注入標(biāo)識(shí)符, 這樣我們就能知道那些字符串是需要解析為函數(shù)了, 如下:

  1. stringify: function(obj: anyspace: number | string, error: (err: Error | unknown) => {}) { 
  2.         try { 
  3.             return JSON.stringify(obj, (k, v) => { 
  4.                 if(typeof v === 'function') { 
  5.                     return `${this.FUNC_PREFIX}${v}` 
  6.                 } 
  7.                 return v 
  8.             }, space
  9.         } catch(err) { 
  10.             error && error(err) 
  11.         } 

this.FUNC_PREFIX 就是我們定義的標(biāo)識(shí)符, 這樣我們?cè)谟?JSON.parse 的時(shí)候就能快速解析函數(shù)了. JSON.parse 也支持第二個(gè)參數(shù), 他的用法和 JSON.stringify 的第二個(gè)參數(shù)類似, 我們可以對(duì)它進(jìn)行轉(zhuǎn)換, 如下:

  1. parse: function(jsonStr: string, error: (err: Error | unknown) => {}) { 
  2.         try { 
  3.             return JSON.parse(jsonStr, (key, value) => { 
  4.                 if(value && typeof value === 'string') { 
  5.                     return value.indexOf(this.FUNC_PREFIX) > -1 ? new Function(`return ${value.replace(this.FUNC_PREFIX, '')}`)() : value 
  6.                 } 
  7.                 return value 
  8.             }) 
  9.         } catch(err) { 
  10.             error && error(err) 
  11.         } 
  12.     } 

new Function 可以把字符串轉(zhuǎn)換成 js 函數(shù), 它只接受字符串參數(shù),其可選參數(shù)為方法的入?yún)?,必填參?shù)為方法體內(nèi)容, 一個(gè)形象的例子:

趣談前端

我們上述的代碼中函數(shù)體的內(nèi)容:

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

之所以要 return 是為了把原函數(shù)原封不動(dòng)的還原, 大家也可以用 eval , 但是出于輿論還是謹(jǐn)慎使用.

以上方案已經(jīng)能實(shí)現(xiàn)前端存儲(chǔ)函數(shù)的功能了, 但是為了更工程化和健壯性還需要做很多額外的處理和優(yōu)化, 這樣才能讓更多人開箱即用的使用你的庫.

最后

為了讓更多人能直接使用這個(gè)功能, 我將完整版 json 序列化方案封裝成了類庫, 支持功能如下:

stringify 在原生JSON.stringify 的基礎(chǔ)上支持序列化函數(shù),錯(cuò)誤回調(diào)

parse 在原生JSON.parse 的基礎(chǔ)上支持反序列化函數(shù),錯(cuò)誤回調(diào)

funcParse 將js對(duì)象中的函數(shù)一鍵序列化, 并保持js對(duì)象類型不變

安裝方式如下:

  1. or npm install xijs 
  2.  
  3. yarn add xijs 

使用:

  1. import { parser } from 'xijs'
  2.  
  3. const a = { 
  4.     x: 12, 
  5.     b: function() { 
  6.       alert(1) 
  7.     } 
  8.  } 
  9.   
  10.  const json = parser.stringify(a); 
  11.  const obj = parser.parse(json); 
  12.  // 調(diào)用方法 
  13.  obj.b(); 

本文轉(zhuǎn)載自微信公眾號(hào)「趣談前端」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系趣談前端公眾號(hào)。

 

責(zé)任編輯:武曉燕 來源: 趣談前端
相關(guān)推薦

2020-03-06 10:05:59

前端Javascript代碼

2009-06-22 11:52:00

javascriptxml

2017-10-27 22:03:35

javascrip

2010-07-30 12:56:02

Flex調(diào)用JavaS

2021-11-16 14:25:38

JavaScript前端

2011-09-02 09:51:21

2010-04-16 11:03:02

Oracle存儲(chǔ)過程

2022-09-04 15:40:39

JavaScrip狀態(tài)模式軟件

2025-01-09 12:00:00

JavaScript前端數(shù)組

2019-07-16 10:35:54

JavaScript進(jìn)階字符串

2011-07-22 14:30:44

存儲(chǔ)過程

2023-10-26 08:06:53

圖片存儲(chǔ)前端

2018-12-06 08:40:43

PythonR函數(shù)編程語言

2021-03-04 08:33:20

JavaScript 前端原生js

2009-06-26 15:55:29

Javascript+

2017-03-15 08:43:29

JavaScript模板引擎

2017-03-20 17:59:19

JavaScript模板引擎

2011-08-30 17:33:10

OracleSAS宏

2023-07-05 16:07:02

JavaScriptWeb 應(yīng)用程序

2024-08-14 08:33:20

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)