Avoiding the Pass-by-Reference JavaScript Pitfall in ServiceNow

A commonly encountered and potentially difficult to debug issue associated with writing scripts in ServiceNow is the case of unintentionally working with a reference to a javascript object property instead of the property’s value. This is most commonly encountered in instances where you are iterating through the results of a GlideRecord query. Below you will find an example of this mistake and an explanation of why it occurs and how to avoid it.
Demonstrating the Issue
Consider the following script:
(function(){
	var incidentCreators = [];
	var output = "";
	var grIncident = new GlideRecord("incident");
	grIncident.addQuery("active", true);
	grIncident.orderByDesc("sys_created_on");
	grIncident.query();
	while(grIncident.next()){
		var user = grIncident.opened_by;
		if(incidentCreators.indexOf(user) == -1){
			incidentCreators.push(user);
		}
	}
	for(var i = 0; i < incidentCreators.length; i++){
		output += incidentCreators[i] + "\n";
	}
	gs.log(output);
})();
In this contrived scenario, we have a script that is intended to query for all active incidents, sorted by “Created” (newest first), and then collect a list of unique sys_ids of users appearing in the “Opened by” field. It would then format the list and print it to the logs. If you were to run this as a background script, you would see that you only receive an output of a single value! Why does this occur?
Explaining the Issue
The issue is brought about by the use of two lines of code:
var user = grIncident.opened_by;
and
incidentCreators.push(user);
Wherein does the issue lie? Well, the first line does not assign the value of grIncident.opened_by to the user variable. It is assigning a reference to the “opened_by” property of the grIncident object and then pushing that reference into the incidentCreators array. As a result, when the “while” loop iterates to the next GlideRecord result and the current value of “user” is compared to the array (via indexOf()), it will always be comparing it’s current value to itself. This results in no values being pushed into the array after the first one.
Comparatively, if the “indexOf()” check didn’t exist, and every value was pushed into the array, you would get an equally unhelpful outcome: Every printed value would appear to be the same. This is because you would have pushed X references to the same object attribute into the array, resulting in the output being the last returned row from grIncident, printed X times.
Avoiding the Issue
To avoid this issue, you must simply ensure that you do not inadvertently use references when you intend to use values. For example, you can fix the issue by replacing:
var user = grIncident.opened_by;
with
var user = grIncident.getValue("opened_by")
or
var user = "" + grIncident.opened_by
By using either of these examples, or another method, to ensure “user” is associated with a string value and not an object reference, you will avoid the issues described above, preventing unintended script behavior.