!geek

Code, learn & share

Javascript functions in JSON configuration

There is no native support for defining functions in JSON. Commonly used approach is to define function as string and use eval() or new Function() to contruct the function. The basic difference between these two are

  • eval() works within the current execution scope. It can access or modify local variables.
  • new Function() runs in a separate scope. It cannot access or modify local variables.

These samples show how the json would differ in these two cases

Using eval()
1
2
3
4
5
6
7
8
9
// JSON config
{
  section: "Additional Details",
  showIf: "function(context) { return context.person.age > 60; }"
}

//This can parsed in javascript as
var showIf = eval('(' + config.showIf + ')')
var shouldShowThisSection = showIf({person: personData}));
Using new Function()
1
2
3
4
5
6
7
8
9
// JSON config
{
  section: "Additional Details",
  showIf: "return context.person.age > 60"
}

//This can parsed in javascript as
var showIf = new Function('context', config.showIf)
var shouldShowThisSection = showIf({person: personData}));

You can use either based on the use case in your application. In bahmni, we went with new Function() for couple of reasons

  • We did not want config code to modify variables in application execution scope by mistake.
  • We wanted to control the function signature such as number of parameters and its name to keep it simple and less error prone.

If you prefer using eval syntax, try vkiryukhin/jsonfn.

Multi line functions

The above examples work fine for sinle line expressions. If you need multiple line functions, you need to tweak it a bit. Firstly, JSON does not support multiline string. The work around is to define an array of strings as shown below.

Multi line functions parsed using new Function()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// JSON config
{
  section: "Additional Details",
  showIf: ["if(context.person.gender === 'M' && context.person.age > 60)",
              "return true;",
           "else if(context.person.gender === 'F' && context.person.age > 55)",
              "return true;",
          "else",
              "return false;"]
}

//This can parsed in javascript as
var showIf = new Function('context', config.showIf.join("\n"))
var shouldShowThisSection = showIf({person: personData}));

Performance

There is not much difference in performance when you define a function using function() {} expression eval(‘function() {}’) or new Function(). Have a look at this benchmark using jsperf.

Comments