Javascript: Alias a function and call it with the correct scope
15th Apr 2019
Today I needed to call one of two possible functions depending on a condition. The interface into these functions was the same (they accepted the same parameters) and they returned data in the same format.
getData() {
const params = {
orgId: this.orgId,
siteId: this.siteId
};
this.dataService.getFullData(params)
.then(result => {
// ... do stuff
})
.catch(this.handleError);
}
Essentially what I want to do is call a leaner "getLeanData" function instead of "getFullData", if no siteId is provided.
There are few approaches to this problem. One would be to change the way getFullData worked, and to move any switching logic into it. The other would be to break up the promise callback functionality and move it into a separate function, and to just have an if block.
I didn't really like either of those approaches and knew that I could alias the function that I wanted to call instead. Here was my first, non working attempt:
getData() {
const params = {
orgId: this.orgId,
siteId: this.siteId
};
let dataFunction = this.dataService.getFullData;
if (typeof params.siteId === 'undefined') {
dataFunction = this.dataService.getLeanData;
}
dataFunction(params)
.then(result => {
// ... do stuff
})
.catch(this.handleError);
}
The above looked like the right approach, and would have worked if:
- This was not an ES6 class. Operating in strict mode means that the scope of the function being called (this) is actually something, and not just the global scope.
- The data functions were local to the ES6 class, and not in a depended upon class
Basically, the function call worked, but it was not executing the dataFunction within the scope of the dataService class - resulting in errors.
Why is this happening?
This happens because when assigning a function to the local variable "dataFunction", only the body of the function, not the object that it needs to be called on, is copied. If the getFullData and getLeanData functions contained non scope specific code, such as a simple console log statement, the behaviour would have been as expected. However in this case, we need the scope of the dataFunction class.
The solution
The solution is to call the function with the scope (officially called the "thisArg") explicitly set. This can be done using the Function.prototype.call() method.
getData() {
const params = {
orgId: this.orgId,
siteId: this.siteId
};
let dataFunction = this.dataService.getFullData;
if (typeof params.siteId === 'undefined') {
dataFunction = this.dataService.getLeanData;
}
dataFunction.call(this.dataService, params)
.then(result => {
// ... do stuff
})
.catch(this.handleError);
}
Calling .bind on the function call will also work with you, but I think the use of .call is a little more readable