Мы используем необязательный второй аргумент replacer для JSON.stringify
для предварительной обработки ключа / значений, выпуская функции в строковом виде:
function stringify_with_fns(obj) {
return JSON.stringify(obj, function(key, value) {
return typeof value === "function" ? value.toString() : value;
});
}
Затем, чтобы превратить строковые функции в действительныефункции на выходе, мы используем дополнительный второй reviver параметр для JSON.parse
, как в
function parse_with_fns(json) {
return JSON.parse(json, function(key, value) {
if (looks_like_a_function_string(value)) {
return make_function_from_string(value);
} else {
return value;
}
});
}
looks_like_a_function_string
это просто регулярное выражение, а первый вырез на make_function_from_string
может использовать eval
:
function looks_like_a_function_string(value) {
return /^function.*?\(.*?\)\s*\{.*\}$/.test(value);
}
function make_function_from_string(value) {
return eval(value);
}
Чтобы избежать eval
, мы можем выделить строку функции, чтобы найти ее аргументы и тело, чтобы мы могли передать их new Function
:
function make_function_from_string(value) {
var args = value
.replace(/\/\/.*$|\/\*[\s\S]*?\*\//mg, '') //strip comments
.match(/\(.*?\)/m)[0] //find argument list
.replace(/^\(|\)$/, '') //remove parens
.match(/[^\s(),]+/g) || [], //find arguments
body = value.match(/\{(.*)\}/)[1] //extract body between curlies
return Function.apply(0, args.concat(body);
}
Тестирование:
x = parse_with_fns(stringify_with_fns({a: function() {var x=1;}}))
x.a
> function anonymous() {var x=1;}
Обратите внимание, однако, что поскольку все функции, созданные с помощью new Function
, находятся в глобальной области видимости, они теряют свою объемную область и замыкания.
Остается вопрос: полезно ли это?Я предполагаю, что это может быть для небольших служебных функций.
Расширение концепции для сериализации / десериализации регулярных выражений или объектов даты оставлено в качестве упражнения.