Automation Script Function and Variable Hoisting

May 3, 2023

Introduction

I prefer developing automation scripts in JavaScript rather than Python. This is partially due to my familiarity bias since I frequently develop with NodeJS. It is also due to my extreme dislike of significant spaces, that these two statements are syntactically different and can lead to compilation errors is maddening.

i = 1

and

i = 1

Hint: One has a tab and the other 4 spaces, I'll let you guess which one is which.

Then there are a number of other minor reasons of varying importance. These include Jython being a functionally dead implementation, having not been updated for over a decade, the lack of native JSON support and the lack of standard library support because of how outdated Jython is.

However, one significant and positive reasons I prefer JavaScript is that it provides function and variable hoisting.

Hoisting

Hoisting is the process whereby the interpreter moves the declaration of a function or variable before its initial invocation. This provides the developer freedom to define utility functions, such as closing MboSets, at the end of the script and call those functions from anywhere in the script.

The script can then be organized in order of importance, in a top down way, with the relevant business logic is at the top of the script and the supporting functions defined from top to bottom, making it easier to read and understand.

For example, in the following script we can call the hoistExample() function before it is declared because the interpreter moves the function definition to the top of the script before execution.

Example

hoistExample();
function hoistExample(){
var message = "Example hoisted message";
service.log_info(message);
}

Output

03 May 2023 14:50:25:881 [INFO] [MXServer] Example hoisted message

Variable Scopes

Variable hoisting occurs within a given scope, either a global or function. Global scoped variables are available anywhere within the script, while function scoped variables are only available within the function where they were declared.

ES6 introduced the addition of block scope with the let and const keywords, but Maximo automation scripts implement ES5, which does not implement this feature.

Declared and Undeclared Variables

Before moving on, it is important to understand the difference between declared and undeclared variables. Declared variables are variables that are declared using the var keyword, all other variables are undeclared variables. Declared variables are confined to the scope in which they are defined, while all undeclared variables are global variables.

This is very important because as noted, variables are hoisted within their scope and this can lead to unanticipated behavior if an global undeclared variable, with global scope, is declared in a function, but is hoisted globally.

In the following example, the variable a is scoped and only available within the hoistExample function, while the variable b is an undeclared variable that is globally scoped and becomes available by the call to the hoistExample when it is assigned. Therefore the log output is number and undefined because b is available outside the function scope while the declared variable a is not.

Example

hoistExample();
service.log_info(typeof b);
service.log_info(typeof a);
function hoistExample(){
var a = 10;
b = 20;
}

Output

03 May 2023 14:52:25:881 [INFO] [MXServer] 20
03 May 2023 14:52:25:891 [INFO] [MXServer] undefined

Variable Hoisting

When the interpreter hoists a variable, the declaration of the variable is hoisted but not the assignment. This means that for the following example instead of logging the message, the value of undefined will be logged.

Example

hoistExample();
function hoistExample(){
service.log_info(message);
var message = "Example hoisted message";
}

Output

03 May 2023 14:58:25:891 [INFO] [MXServer] undefined

This is because the JavaScript interpreter dutifully hoists the declaration of the variable, but leaves the assignment in place, producing the following code.

hoistExample();
function hoistExample(){
var message;
service.log_info(message);
message = "Example hoisted message";
}

To avoid this, variables should be declared and assigned before they are referenced, as shown in the example below.

hoistExample();
function hoistExample(){
var message = "Example hoisted message";
service.log_info(message);
}

Functions

Functions come in two basic forms:

  1. Function declarations
  2. Function expressions

Function Declarations

Function declarations start with the function keyword and are the most common type of function in automation scripts. These are hoisted to the top of the script, which is why they may be called from anywhere within the script. Because function declarations are hoisted first, they take precedence over any other variable or expression declarations.

Example

hoistedFunction();
function hoistedFunction() {
service.log_info('Example hoisted message.');
};

Output

03 May 2023 14:58:25:891 [INFO] [MXServer] Example hoisted message.

Function Expressions

Function expressions are not hoisted because like variables the assignment of the expression occurs where it is declared. For the following example the TypeError: expression is not a function error is thrown because hoistedExpression has not yet been assigned.

Example

hoistedExpression();
var hoistedExpression = function() {
service.log_info('Example hoisted message.');
};

Output

03 May 2023 14:58:25:891 [INFO] [MXServer] TypeError: expression is not a function.

Strict Mode

Nashorn, which implements ES5 of the JavaScript specification, provides the "use strict" declaration. By including the "use strict" declaration the interpreter will throw an explicit Reference error when a variable is unassigned. Revisiting our previous example with the "use strict" declaration will now throw a Reference error instead of silently failing.

Note that the "use strict" includes the double quotes (") surrounding the statement.

"use strict";
hoistExample();
function hoistExample(){
message = "Example hoisted message";
service.log_info(message);
}

Within the context of Maximo automation scripts though this it maybe of limited utility as unassigned implicit variables such as responseBody will cause a Reference error, although it can be resolved by declaring the implicit variable at the start of the script as shown below.

"use strict";
var responseBody;
hoistExample();
responseBody = "Hoisting Example";
function hoistExample(){
message = "Example hoisted message";
service.log_info(message);
}

Conclusion

In this post we examined JavaScript function and variable hoisting. We reviewed how hoisting works for declared and undeclared variables, as well as function declarations and function expressions. We reviewed how declaration and assignment order impacts the hoisting process and how it may lead to subtle and unexpected behavior. Finally we reviewed how using the "use strict" declaration can help avoid potential errors, but may also cause unexpected Reference error errors for Maximo's unassigned implicit variables and how that may be mitigated.

In upcoming posts we will be looking at how to use Polyfill function to bring ES6 and later implementation functions to Maximo automation scripts. Understanding function and variable hoisting as well as the impact of function and variable ordering is very important to understand how to properly implement Polyfill functions.

If you have any questions or comments please reach out to us at [email protected]

In the time it took you to read this blog post...

You could have deployed Opqo, our game-changing mobile solution for Maximo.

Opqo is simple to acquire, simple to deploy and simple to use, with clear transparent monthly pricing that is flexible to your usage.