Reputation: 3312
As part of the web app I'm building, multiple commands can come in from the server at once.
When these commands are processed, data will tend to be updated and then fairly heavy page HTML generation can occur.
At the moment when multiple similar commands come in, the program will process the data, then parts of the page are regenerated each time. This can lead to a lot of wasted processing time.
As such, I'm trying to make a callstack, so when a command is processed it looks to see if the function that the command triggers is in the stack and if it isn't found it adds it.
My problem is with keeping the called function from the callstack in the correct scope while also being able to eliminate duplicates.
Base Code:
var tools = {
functions:{
function1: function(){
return this;
},
function2: function(){
}
},
unique: function(arr){
var returnArr=[],
x, i;
for (x=0; x<arr.length; x++){
for (i=(x+1); i<arr.length; i++){
if (arr[x]===arr[i]){
arr.splice(i, 1);
}
}
returnArr.push(arr[x]);
}
return returnArr;
}
}
Example 1:
var callstack=[tools.functions.function1, tools.functions.function1];
callstack = tools.unique(callstack);
for (var x=0; x<callstack.length; x++){
console.log(callstack[x](), "should equal tools.functions");
}
This fails as "this" will return [function()]
Example 2:
var callstack=[
(function(){return tools.functions.function1()}),
(function(){return tools.functions.function1()})
];
callstack = tools.unique(callstack);
for (var x=0; x<callstack.length; x++){
console.log(callstack[x](), "should equal tools.functions");
}
This fails because you can't ensure that the functions are unique, so it'll still run the function twice.
This can be worked around by using two arrays (one that keeps track of the names of the functions, one that holds the encapsulated functions) and keeping them in sync, but I can't help but feel there must be a cleaner way using .call but I can't see it.
JSFiddle: http://jsfiddle.net/Kj6E8/
Upvotes: 0
Views: 82
Reputation: 22956
Generically speaking it is impossible to preserve the value of this
in the way that you require. As soon as you push those functions into the array, the object that those functions once resided on is lost.
If you are only calling functions on a single object, you need to bind your functions to that object in some way. There are many ways of doing that, for example, you could pass the object to bind those function to in your unique function.
var tools = {
functions:{
function1: function(){
return this;
},
function2: function(){
}
},
unique: function(arr, o){
var returnArr=[],
x, i;
for (x=0; x<arr.length; x++){
for (i=(x+1); i<arr.length; i++){
if (arr[x]===arr[i]){
arr.splice(i, 1);
}
}
returnArr.push(this.bind(arr[x], o));
}
return returnArr;
},
bind: function(f, o) {
return function () {
return f.apply(o, arguments);
}
}
}
console.info("Example 1:");
var callstack=[tools.functions.function1, tools.functions.function1];
callstack = tools.unique(callstack, tools.functions);
console.log("Callstack:",callstack);
for (var x=0; x<callstack.length; x++){
console.log(callstack[x](), "should equal tools.functions");
}
How and when you infer the value of this
really depends on when you have that object in scope. Presumably you are more likely to have that object in scope during the callstack creation phase, and perhaps not necessarily at the point where you wish to invoke the functions in the callstack. If that is the case, binding as early as possible (i.e. during the reduction to a unique callstack as demonstrated) seems like a sensible option.
Upvotes: 1
Reputation: 108490
In JavaScript, this
is defined from the outside caller, so when you do:
callstack[x]()
You are bringing the window
scope into the functions. If you want to bring in the tools.functions
object, you need to do:
callstack[x].call(tools.functions);
Upvotes: 1