I pass an anonymous function-A as parameter to another function-B. Even after I call B.destroy, function A exists and gets executed, it even as an live anonymous scope to itself. I found this unexpected behavior through debugger. Following is the code.
var builder.record.verifyExplorer = new builder.VerifyExplorer(
window.bridge.getRecordingWindow(),
builder.getScript().seleniumVersion,
function(step) {
builder.getScript().addStep(step);
builder.stepdisplay.update();
// Don't immediately stop: this would cause the listener that prevents the click from
// actually activating the selected element to be detached prematurely.
setTimeout(function() { builder.record.stopVerifyExploring(); }, 1);
window.bridge.focusRecorderWindow();
}
);
I destroy the above function by defining stopVerifyExploring to be
builder.record.stopVerifyExploring = function() {
builder.record.verifyExploring = false;
builder.record.verifyExplorer.destroy();
builder.record.verifyExplorer = null;
builder.record.continueRecording();
};
Even after verifyExplorer.destroy is called function(step) is live in anonymous scope and gets executed and does all unwanted things.
I have this weird behavior by replacing
jQuery(frame.document).bind(l, {}, ae.listeners[l], true);
with
frame.document.addEventListener(l,ae.listeners[l],true);
in verifyExplorer. How does me changing the above piece code lead to unexpected behavior?
All this is part of me fixing a bug in an open source project sebuilder. Also related post
The behavior isn't unexpected at all, you've done nothing to cancel the timer.
If you want to cancel the timer, save the return value of setTimeout and then pass that value into clearTimeout later when/if you want to cancel the timer.
What's going on:
Functions and objects exist for as long as anything has a reference to them. When you do:
setTimeout(function() { builder.record.stopVerifyExploring(); }, 1);
...you're saving a reference to that anonymous function in the browser's timer handling stuff. That function has a reference to the context in which it was created (and all containing contexts). So even if you do verifyExplorer = null, that has no effect on anything that the verifyExplorer object used to refer to at all, nor anything that was referring to that object. It just clears the reference to that object from that specific variable. If there are other outstanding references (and there are), the object is kept in memory.
In this case, if the only outstanding reference is the function you've given to setTimeout, then clearing the timeout (probably in destroy) will release the browser's reference to that function, which releases that function's references to the contexts it closes over, and those things are eligible for garbage collection.
More (on my blog): Closures are not complicated
Related
Here is the app I'm referring to:
I am trying to fundamentally understand the bind method in Javascript.
My understanding when I play around with it in the console is that bind returns a copy of the function, with "this" bound to whatever you pass into bind.
function logThis(){
console.log(this)
}
logThis.bind({today: 'Tuesday'})
//Will return a copy of the logThis function, with 'this' set to the
{today:'Tuesday'} object. The code does not run right away though.
var explicitlyLogThis = logThis.bind({today: 'Tuesday'});
explicitlyLogThis(); //This will run the code and display the {today: 'Tuesday'} object to the console.
This is my understanding so far. I understand that to actually run this new function that has 'this' explicitly bound using the bind method, you need to set it to a variable and then run it.
I see a contradiction when I look at the app in the above link. If you look at the bindEvents method on line 56, we have .on('keyup', this.create.bind(this)). I understand that we have to set 'this' to App when we run the create method because jQuery defaults to setting 'this' to the jQuery object itself. So this line is actually the same as: $('#new-todo').on('keyup', App.create.bind(App)).
That isn't where my confusion is. My question is:
How exactly are these copies of the functions with 'this' set to App actually being called? The app does not set them to a variable and then call that variable the way I had to when I was working in the console.
It just invokes the bound functions directly as soon as an event occurs on one of the jQuery elements. But I thought writing it this way would just return a copy of the function, and not run the function itself, if I am basing my assumptions on what I have figured out in the code I wrote above. I thought in order to invoke the function immediately, you would need to use call or apply.
I also realize that the app runs the bindEvents method when it starts (see line 46). So I understand that when you start the app, copies of the various functions are created with the correct 'this' bound to the functions. But...when/how do they actually get invoked without assigning them to variables? How are these copies accessed?
I think I have a flawed understanding of the bind method, so I would love some help. Thanks!
It sounds like you understand bind well enough. Perhaps there is some confusion with passing anonymous functions. As you know calling bind returns a new function and this can optionally be stored as a variable or passed as a function argument.
In the example below btn1 accepts a bound function as you've seen. This could also be written in a more long hand fashion with btn2. They're identical. btn3 doesn't receive a bound function, when its clicked its context is the button element, this looses all visibility of MagicalApp fucntions.
<button id="example1">button one bound</button>
<button id="example2">button one bound</button>
<button id="example3">button two unbound</button>
<script>
class MagicalApp {
add() {
console.log('this could do addition');
}
}
const app = new MagicalApp();
function contextOfEvent(event) {
console.log('contextSensitive', this.add)
}
const btn1 = document.querySelector("#example1");
btn1.addEventListener('click', contextOfEvent.bind(app));
const btn2 = document.querySelector("#example2");
const btn2ClickHandler = contextOfEvent.bind(app)
btn2.addEventListener('click', btn2ClickHandler);
const btn3 = document.querySelector("#example3");
btn3.addEventListener('click', contextOfEvent);
</script>
Coming from a C++ background, trying to work with an OO language that doesn't have explicit typing is a little more than a headache.
So I have dynamic elements for a webpage that are "controlled" by objects since there are tons of stuff I need to manage on each for it to work. The element is just the visual output of the data inside of the object itself, that's all I really need it for.
Except that I need the object to perform an internal function when it's clicked. That seems to be the biggest source of my headache thus far.
Javascript:
function onClick(file) //The external onClick function I use to try to get it to call from.
{
file.state = INUSE;
file.checkState();
}
function fileObject () { //The file object itself
this.element;
this.newElement();
//initialize stuff for the object
}
fileObject.prototype.newElement = function() { //creates a new element and sets its event listener
this.element.click(function() {onClick(this)});
}
fileObject.prototype.checkState = function() {/*does stuff*/} //apparently this is "not a function"
The error I get exactly is "file.checkState is not a function" from Firefox's console panel.
I'm still new to javascript, but after doing some debugging, I've come to find out that it's explicitly the onClick(this) function that is causing all of the errors. When used with something else, the onClick function works perfectly, but for some reason, the this keyword doesn't appear to actually be sending the reference to the fileObject since all checks show file being undefined when inside of the onClick scope.
Is there something fundamentally wrong about the way I'm trying to do this or am I just missing a step (or adding something that I don't need) that will help get this snippet working.
So you know, your initial problem isn't actually handling the action, but listening to it. click will trigger a synthetic click event, rather than liste for one.
You want ... .element.addEventListener("click", callback); that said, you face a second problem, immediately thereafter.
I will leave my example code as you've written it to not confuse the matter...
But when you see click( ) know that I mean subscribing with addEventListener, if element really does mean a browser DOM element. If it's not a standard browser element, and your own API, then ignore the previous portion, and carry on.
this is dynamically bound at the invocation time of the function (not at definition time).
The nearest function, scoped above, is your callback function that you are passing into .click( ... ).
Which is entirely different than the this which you mean outside of the callback.
Whatever is on the left-hand side of the dot is the this context for the duration of that particular invocation.
Needless to say, click() doesn't know enough to bind the this you mean, to the left-hand side of your callback.
The solution (or one of many) is to use lexical scoping and/or closure to retain the value of the object you mean.
// easy but messier
var fileObject = this;
... .click(function () { onClick(fileObject); });
// Cleaner with thunks:
function clickHandler (onClick, obj) {
return function () { onClick(obj); };
}
... .click(clickHandler(this));
Coming from c++ the way Javascript handles this will seem a little crazy, it looks like here you need to tell the function you've defined what this is - like so:
this.element.click(function() {onClick(this)}.bind(this));
I just stumbled on the IndexedDB example on MDN which contains the following:
function openDb() {
var req = indexedDB.open(DB_NAME, DB_VERSION);
req.onsuccess = function (evt) {
// Better use "this" than "req" to get the result
// to avoid problems with garbage collection.
// db = req.result;
db = this.result;
};
// Rest of code omitted for brevity
}
What is the problem with the garbage collector that should better be avoided?
This advice looks weird: the object the req variable refers to (the same the this would refer to) as well as the anonymous function objects (which are hold by onsuccess, onerror and onupgradeneeded properties) would all be garbage collectible simultaneously as soon as the query has completed and callbacks have been invoked.
Technically - req represents another reference to the object; practically it cannot cause any "problems with garbage collection".
To summarize: it's neither an "optimisation" nor "micro optimisation", both would perform equally.
As far as I can tell if you reference 'req', 'openDb' invocation scope is tied to 'onsuccess' (as parent scope) so you create a closure. If - on the other hand - you reference only 'this', the 'openDb' invocation scope can be discarded as soon as you exit the function.
What might be causing confusion is that the object refered to by 'req' lives beyond the lifetime of 'openDb' - it is not used exclusiveley from within that function.
Background:
I've written a javascript object called Step to implement a step-type event for any operation I might need to use for my website, e.g. smooth-motion functions. I can register functions in it to run each step by using the Step object's method, registerFunction(func).
To handle running the functions every step, the Step object has another method called run().
In this method I run through the list of registered functions, run them, and then call the setTimeout(...) function, passing the reference to the run function to recall it for the next step.
Problem:
The first 'step' is run by the onload event (attribute) in the <body> tag of my html page (indirectly, though: Step.run is called by a function initiateJS() which is called by onload). Nothing goes wrong in the first step. However, by the second step, the variables within the Step object seem to have been disposed; they all become undefined.
Questions:
I was thinking it might be due to the garbage collector? Maybe it somehow loses its reference?
Also, is using setTimeout(...) even the best method of making a step-type event implementation?
Details:
The Step object is declared in the js document somewhat as:
var Step = {
myVars: "somevalues",
anArray: [],
run: function() {
//run the registered functions
setTimeout(this.run,this.interval);
};
ALSO, in the next step, the Step object is still existent.
Some other minor details:
I'm using Chrome
I'm making my website with XAMPP full
I'm using Windows 7
setTimeout runs code in the global context. this is no longer defined the next time run() executes. You'll need to refactor your code to either declare variables on some kind of global object, or to pass references into the run function itself.
edit: since you said Step is global, this should work:
run: function() {
//run the registered functions
setTimeout(Step.run, Step.interval);
}
Change your run function to this:
run: function() {
var that = this;
setTimeout(that.run, that.interval);
}
The setTimeout function redefines the this variable and this is the simplest way I can think of to work around this issue.
If i have a function like this
function do(callback) {
//do stuff
callback();
}
and then I pass in an anonymous function:
do(function() { //do something else });
does that anonymous function ever get collected during the lifespan of the page? If not, how can i make it available for GC?
do I have to do this?
var h = function() { //do something };
do(h);
delete h;
Do I even have to worry about this? I am building a web app that has a long lifespan, makes a lot of ajax calls keeps objects for a while and doesn't really require a page refresh to navigate thru. So I'm trying to figure out if I might fall into a memory leak monster.
The only reference to the anonymous function is the function argument, and that disappears when the function finishes, so your callback will be available for garbage collection after that. Except when something else gets a reference to it, which can happen easily with closures:
function doo(callback) {
$.get(url, function() {
// callback is visible here!
});
callback();
}
doo(function() { /* do something else */ });
callback (along with the whole scope created by calling doo) must stay in the memory, because the inner function can reference it through the closure; it can only be garbage collected when the inner function is garbage collected, and since that function is a property of the jqXHR object, that object must be garbage collected before that, and who knows when that will happen...
Update You can avoid unnecessary closures by not defining your functions inside other functions:
var func = function() {
// callback is not visible here
}
function doo(callback) {
$.get(url, func);
callback();
}
doo(function() { /* do something else */ });
Watch out for circular references, otherwise the GC for the browser will clean those up. Closures make it really easy to create a circular reference, and that might be trapped in memory even if you browse away from the page that created it. So, web applications that stay on-screen for long periods of time are especially vulnerable.
Check out the section "Memory leaks" here: https://developer.mozilla.org/en/A_re-introduction_to_JavaScript.
I've designed quite a few static-page web applications. I've found that even when you don't have to clean up objects and event handlers (ie you're sure there is no circular reference), it can't hurt. It usually only adds a couple of extra lines of code, and it keeps memory use and efficiency at the forefront of your mind as you write your code. This is something of a shift for web developers because we usually don't have to think about this kind of thing very much when creating a website.