簡體   English   中英

如何正確序列化Javascript curried arrow函數?

[英]How to correctly serialize Javascript curried arrow functions?

const makeIncrementer = s=>a=>a+s
makeIncrementer(10).toString()    // Prints 'a=>a+s'

這將使得無法正確反序列化(我會期待類似a=>a+10東西。有沒有辦法做正確的?

這是一個很好的問題。 雖然我沒有完美的答案,但您可以通過一種方式獲得有關參數的詳細信息,即創建一個構建函數,為您存儲必要的詳細信息。 不幸的是,我無法找到一種方法來了解哪些內部變量與哪些值相關。 如果我弄明白我還會更新其他內容:

 const makeIncrementer = s => a => a + s const builder = (fn, ...args) => { return { args, curry: fn(...args) } } var inc = builder(makeIncrementer, 10) console.log(inc) // logs args and function details console.log(inc.curry(5)) // 15 


更新:這將是一個龐大的任務,但我意識到,如果你擴展上面的構建器的想法,你可以編寫/使用一個函數字符串解析器,可以采取給定的args和外部函數,並重寫日志到序列化版本。 我在下面有一個演示, 但它不適用於實際用例! 我已經完成了一個簡單的字符串查找/替換,而您需要使用實際的函數解析器來正確替換。 這只是你如何做到這一點的一個例子。 請注意,我還使用了兩個增量變量來展示如何進行倍數。

 function replaceAll(str, find, replace) { return str.replace(new RegExp(find, 'g'), replace) } const makeIncrementer = (a, b) => c => c + a + b const builder = (fn, ...args) => { // get the outer function argument list var outers = fn.toString().split('=>')[0] // remove potential brackets and spaces outers = outers.replace(/\\(|\\)/g,'').split(',').map(i => i.trim()) // relate the args to the values var relations = outers.map((name, i) => ({ name, value: args[i] })) // create the curry var curry = fn(...args) // attempt to replace the string rep variables with their true values // NOTE: **this is a simplistic example and will break easily** var serialised = curry.toString() relations.forEach(r => serialised = replaceAll(serialised, r.name, r.value)) return { relations, serialised, curry: fn(...args) } } var inc = builder(makeIncrementer, 10, 5) console.log(inc) // shows args, serialised function, and curry console.log(inc.curry(4)) // 19 

您不應該序列化/解析函數體,因為這很快就會導致安全漏洞。 序列化閉包意味着序列化其本地狀態,即必須使閉包的自由變量對於周圍的作用域可見:

 const RetrieveArgs = Symbol(); const metaApply = f => x => { const r = f(x); if (typeof r === "function") { if (f[RetrieveArgs]) r[RetrieveArgs] = Object.assign({}, f[RetrieveArgs], {x}); else r[RetrieveArgs] = {x}; } return r; } const add = m => n => m + n, f = metaApply(add) (10); console.log( JSON.stringify(f[RetrieveArgs]) // {"x":10} ); const map = f => xs => xs.map(f) g = metaApply(map) (n => n + 1); console.log( JSON.stringify(g[RetrieveArgs]) // doesn't work with higher order functions ); 

我使用Symbol來使新屬性不會干擾程序的其他部分。

如代碼中所述,您仍然無法序列化高階函數。

到目前為止,結合兩個答案的想法,我設法生產一些有用的東西(雖然我還沒有徹底測試過):

const removeParentheses = s => {
    let match = /^\((.*)\)$/.exec(s.trim());
    return match ? match[1] : s;
}

function serializable(fn, boundArgs = {}) {
    if (typeof fn !== 'function') return fn;
    if (fn.toJSON !== undefined) return fn;

    const definition = fn.toString();
    const argNames = removeParentheses(definition.split('=>', 1)[0]).split(',').map(s => s.trim());

    let wrapper = (...args) => {
        const r = fn(...args);

        if (typeof r === "function") {
            let boundArgsFor_r = Object.assign({}, boundArgs);
            argNames.forEach((name, i) => {
                boundArgsFor_r[name] = serializable(args[i]);
            });
            return serializable(r, boundArgsFor_r);
        }
        return r;
    }

    wrapper.toJSON = function () {
        return { function: { body: definition, bound: boundArgs } };
    }
    return wrapper;
}

const add = m => m1 => n => m + n * m1,
    fn = serializable(add)(10)(20);

let ser1, ser2;

console.log(
    ser1 = JSON.stringify(fn)          // {"function":{"body":"n => m + n * m1","bound":{"m":10,"m1":20}}}
);

const map = fn => xs => xs.map(fn),
    g = serializable(map)(n => n + 1);

console.log(
    ser2 = JSON.stringify(g)   // {"function":{"body":"xs => xs.map(fn)","bound":{"fn":{"function":{"body":"n => n + 1","bound":{}}}}}}
);

const reviver = (key, value) => {
    if (typeof value === 'object' && 'function' in value) {
        const f = value.function;
        return eval(`({${Object.keys(f.bound).join(',')}}) => (${f.body})`)(f.bound);
    }
    return value;
}

const rev1 = JSON.parse(ser1, reviver);
console.log(rev1(5));   // 110

const rev2 = JSON.parse(ser2, reviver);
console.log(rev2([1, 2, 3]));   // [2, 3, 4]

這適用於箭頭函數,它沒有參數的默認初始值設定項。 它也支持更高階的功能。 在將原始函數應用於任何參數之前,仍然必須能夠將原始函數包裝成可serializable 感謝@MattWay和@ftor的寶貴意見!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM