欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

前端進階之教你利用javascript存儲函數(shù)

 更新時間:2021年11月30日 12:07:47   作者:徐小夕  
這篇文章主要給大家介紹了關于利用javascript存儲函數(shù)的相關資料,文中通過實例代碼介紹的非常詳細,對大家學習或者使用js具有一定的參考學習價值,需要的朋友可以參考下

前言

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

背景介紹

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

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

在 數(shù)據(jù)驅動視圖 的時代, 這三個要素的關系往往如下圖所示:

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

{
    "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序列化的方式), 但是如何將函數(shù)也一起保存呢? 保存好了函數(shù)如何在頁面渲染的時候能正常讓 js 運行這個函數(shù)呢?

實現(xiàn)方案思考

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

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

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

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

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

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

js存儲函數(shù)方案設計

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

  • 如果返回一個 Number, 轉換成相應的字符串作為屬性值被添加入 JSON 字符串
  • 如果返回一個 String, 該字符串作為屬性值被添加入 JSON 字符串
  • 如果返回一個 Boolean, 則 "true" 或者 "false" 作為屬性值被添加入 JSON 字符串
  • 如果返回任何其他對象,該對象遞歸地序列化成 JSON 字符串,對每個屬性調用 replacer 方法。除非該對象是一個函數(shù),這種情況將不會被序列化成 JSON 字符
  • 如果返回 undefined,該屬性值不會在 JSON 字符串中輸出

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

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

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

因為我們將函數(shù)轉換為字符串了, 我們在反解析時就需要知道哪些字符串是需要轉換成函數(shù)的, 如果不對函數(shù)做任何處理我們可能需要人肉識別.

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

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

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 的時候就能快速解析函數(shù)了. JSON.parse 也支持第二個參數(shù), 他的用法和 JSON.stringify 的第二個參數(shù)類似, 我們可以對它進行轉換, 如下:

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 函數(shù), 它只接受字符串參數(shù),其可選參數(shù)為方法的入?yún)?,必填參?shù)為方法體內容, 一個形象的例子:

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

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

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

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

最后

為了讓更多人能直接使用這個功能, 我將完整版 json 序列化方案封裝成了類庫,

支持功能如下:

  • stringify 在原生JSON.stringify 的基礎上支持序列化函數(shù),錯誤回調
  • parse 在原生JSON.parse 的基礎上支持反序列化函數(shù),錯誤回調
  • funcParse 將js對象中的函數(shù)一鍵序列化, 并保持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();

總結

到此這篇關于利用javascript存儲函數(shù)的文章就介紹到這了,更多相關用javascript存儲函數(shù)內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論