I am writing a web application that has a static outer "shell" and a dynamic content section. The dynamic content section has many updates as users navigate the system. When a new content block is loaded, it may also optionally load another JavaScript file. In the name of good housekeeping, I remove script blocks from the DOM that apply to old content blocks, since that JavaScript is no longer needed.
The problem comes next, when I realized that although I have removed the <script> element from the DOM, the JavaScript that was previously evaluated is still available for execution. That makes sense of course, but I'm worried that it may cause a memory leak if the users navigate to a lot of different sections.
The question then, is should I be worried about this situation? If so, is there a way to force the browser to cleanup stale JavaScript?
<theory>You could go with a more object-oriented approach, and build the model in a way that each block of javascript blocks come in as their own objects, with their own methods. Upon unloading it, you simply set that object to null.</theory>
(This is fairly off-the-cuff.)
Memory use is indeed an issue you need to be concerned with in the current browser state of the art, although unless we're talking about quite a lot of code, I don't know that code size is the issue (it's usually DOM size, and leftover event handlers).
You could use a pattern for your loadable modules that would make it much easier to unload them en mass -- or at least, to let the browser know it can unload them.
Consider:
window.MyModule = (function() {
alert('This happens the moment the module is loaded.');
function MyModule() {
function foo() {
bar();
}
function bar() {
}
}
return MyModule;
})();
That defines a closure that contains the functions foo and bar, which can call each other in the normal way. Note that code outside functions runs immediately.
Provided you don't pass out any references to what's inside the closure to anything outside it, then window.MyModule will be the only reference to that closure and its execution context. To unload it:
try {
delete window.MyModule;
}
catch (e) {
// Work around IE bug that doesn't allow `delete` on `window` properties
window.MyModule = undefined;
}
That tells the JavaScript environment you're not using that property anymore, and makes anything it references available for garbage collection. When and whether that collection happens is obviously implementation-dependent.
Note that it will be important if you hook event handlers within the module to unhook them before unloading. You could do that by returning a reference to a destructor function instead of the main closure:
window.MyModule = (function() {
alert('This happens the moment the module is loaded.');
function foo() {
bar();
}
function bar() {
}
function destructor() {
// Unhook event handlers here
}
return destructor;
})();
Unhooking is then:
if (window.MyModule) {
try {
window.MyModule();
}
catch (e) {
}
try {
delete window.MyModule;
}
catch (e) {
// Work around IE bug that doesn't allow `delete` on `window` properties
window.MyModule = undefined;
}
}
If you save the evaluated code in namespaces, such as:
var MYAPP = {
myFunc: function(a) { ... }
}
"Freeing" the whole thing should be as simple as setting MYPP to some random value, ala
MYAPP = 1
This does depend on there being no other means of referencing the variable, which isn't trivial
How about loading the JS files into an iframe? Then (in theory, never tested it myself) you can remove the iframe from the DOM and remove the "memory" it's using.
I think... or I hope...
If you are worried about memory leaks then you will want to make certain that there is no event handlers in the code you want removed referring to the still existing dom tree.
It may be that you need to keep a list of all event handlers your code added, and before unloading, go through and remove the event handlers.
I have never done it that way, I always worry about when I remove nodes that there is still a reference.
Here is a good article on javascript memory leaks:
http://javascript.crockford.com/memory/leak.html
JavaScript interpreters have garbage collectors. In other words, if you don't reference anything, it won't be keeping them around.
One of the reasons why it is good to use JSON with a callback function (JSONP).
example, if you HTTP response for each JS is:
callback({status: '1', resp: [resp here..]});
And if callback() does not create a reference to the JSON object passed in as an argument, it will be garbage collected after the function completes.
If you really need to make a reference, then you probably need that data around for some reason - otherwise you would/should NOT have referenced it in the first place.
The methods mentioned to namespace objects just creates a reference that will be persisted until the reference count comes to 0. In other words, you have to track every reference and delete it later, which can be hard when you have closures and references from DOM lying around. Just one reference will keep the object in memory, and some simple operations may create references without you realizing it.
Nice discussion. Clears up a lot of things. I have another worry, though.
If I bind window.MyModule.bar() to an event, what happens if the event accidentally gets triggered after window.MyModule is deleted? For me, the whole point of namespacing and separating js into dynamically loaded modules is to avoid triggering event handlers cross-module by mistake.
For example, if I do (excuse my jQuery):
$('.some-class').click(window.MyModule.bar);
What happens if I delete window.MyModule, load another module, and click on an element which accidentally has a class called some-class?
Related
I have a website that has a lot of popups, whenever a user load a popup (via ajax) there is a JavaScript code also loaded in the popup, I want this JavaScript code to be removed when its popup removed.
Main site code:
<body>
...
<div id="popup" style="display:none">
</div>
...
</body>
and then a loader in js file:
$.ajax('url',{
success:function(data){
$("#popup").show().html(data);
};
});
one of the views that ajax call and push it in the popup:
some html codes
<script>
$(document).on('click',function(){
console.log("document click");
});
</script>
when the user closes the popup, I use this code to empty the popup:
$("#popup").hide().html("");
but this doesn't end the on click event that was loaded, and any functions in the view will still exist in the memory and can be called!
this is an issue because I have a lot of popups here, and JavaScript eats the memory after a while, so I need to refresh the browser after using the website for some time, plus, events are duplicated whenever the popup reloaded!
If it not possible to clear these JS from the memory, are there any workaround for it?
What I get from your question (if I understood it correctly) is you need a way to clean up your memory from unused functions (maybe variables too) with some events listeners, Here is my approach:
#1 - Use removeEventListener
removeEventListener is a method used to remove unwanted events listeners from event target for example:
// add click event to the body
document.body.addEventListener('click', function clickHandler(){
console.log("document click");
});
// remove click event from the body
document.body.removeEventListener("click", clickHandler)
#2 - Garbage Collection
JavaScript does not provide a direct way to clean up the memory from unnecessary data (function declaration or variables declaration) but we can trick it using Garbage Collection. Garbage Collection uses an algorithm for cleaning up the memory from unnecessary data called Mark and Sweep. Basically this algorithm clean up the memory through marking of all the nodes (Objects) that are not reachable though the root node (which is the global object in JavaScript) and remove them from the memory. We can use this to our advantage by using two things (IIEF and Block-Scoping).
IIFE
IIFE is an acronym for Immediately Invoked Function Expression. If you go more deep about IIFE you will find that the function created inside (...) are hiding in a different scope other than the global scope, which means that by creating a function as an IIFE we will prevent the global Object from accessing it which means it will get removed from memory by the Garbage Collector after it gets executed.
Take a look at this example:
(function foo() {
// your code goes here
})()
console.log(foo) // ReferenceError: foo is not defined
Block-Scoping
Introduction to Scope
A block statement is used to group zero or more statements. The block is delimited by a pair of braces {...} and may optionally be labelled.
If you declared your variables inside it using the let or const keywords, Your variables will be not reachable by the global object and then can be removed from memory by the Garbage Collector after it gets executed.
Take a look at this example:
function foo(data) {
// do something with the data
}
{
let someBigData = []
foo(someBigData)
}
console.log(someBigData) // ReferenceError: someBigData is not defined
Let's say a web page has the following code loaded from an external JavaScript file:
(function(foo) {
function bar() {
// Do something
}
function baz() {
// Do something else
}
})(window, window.jQuery);
For arguments sake, let's say I'm writing a chrome extension that will load a script I created with changes to some of the existing code; perhaps changes to the bar function but not baz. I could copy all of the existing code, make my changes, and load the modified JavaScript after blocking the original, but I don't want to do that if I can avoid it.
Is there a way to modify the behaviour of existing nested functions without loading a whole copy of the modified code?
Not really, no.
This pattern, an immediately-invoked anonymous closure:
(function() {
// stuff
})();
is specifically used to hide the internal contents from the global namespace. Once that code finishes executing, there are no references that can access its internal contents.
So your plan of blocking the script and then providing your full copy seems to be the only one you can do.
I'm debugging an app that uses .NET's scriptmanager.
It may be a glitch in firebug, but when I read through the code there are a lot of lines like the following:
// anonymous functions not attached as handlers and not called immediately
function () {
//code
}
// named functions added as methods
myObj = {
myMethod: function myFunctionName() {
//code
}
}
Are these lines valid and, if so, what do they do and what possible reason would there be for coding like this (and I won't accept "It's microsoft - what d'you expect" as an answer)?
This might be worth a read: How does an anonymous function in JavaScript work?
They are there because some busy programmer was intending to do something and ran out of time, but left the stub as a reminder of work to be done. They do nothing as of yet.
or to watermark the code for checks that are done elsewhere in the logic
or simply put there to obfuscate...
I want to log all errors in my browser side JavaScript code. I saw a lot of discussions here and on the net about window.onerror and it is clear it does not work cross browser. So, I plan to wrap top level entry functions with try - catch. Trouble is, a lot of my code is event handlers. I have not tested it yet, but I am pretty sure that no matter where the event handler function is defined, a thrown error will fire out directly to the browser implementation that calls it, not to event function declaring code. My only choice is to declare throw, catch and error log calls in every error handler even the tiniest anonymous one. I don't like that one bit.
A possible solution:
I use one method to cross browser register events. I can modify it to do something like this:
function registerEventHandler(object, handlerRef) {
var wrapperFunction = function(evt) {
try {
handlerRef(evt);
} catch {
logError(error);
}
registerEvent(object, wrapperFunction);
}
There is one major problem with that implementation. I often keep references to event handler function in order to deregister them later. This will not work as the function registered as the handler will be the wrapper, not the original one. The answer to this is to implement a wrapper -> wrapped mapping object and use it on unregister.
The question:
I dare you JavaScript magicians to come up with more clever solution than that. Maybe it could be done by somehow augmenting the event handler function before registration? This is a far as my JavaScript knowledge goes. What about yours?
I often keep references to event
handler function in order to
deregister them later. This will not
work as the function registered as the
handler will be the wrapper, not the
original one.
Why is this a problem? once the function is wrapped in the error handling, you dont really care about the original function anymore. The wrapper keeps a reference to your original function, and the wrapper is what is registered, and the wrapper is what needs to be unregistered.
Just keep a reference to your wrapper function you generate because its the only one that matters.
Also making it it's own function will make this pattern far more reusable
var protectErrors = function(fn) {
var that = this;
return function() {
try {
fn.apply(that, arguments);
} catch(error) {
logError(error);
}
};
};
var registerEventHandler = function(object, handlerRef) {
var wrapperFunction = protectErrors(handlerRef);
registerEvent(object, wrapperFunction);
};
protectErrors(fn) will return a function that runs the original in whatever context it was called in, and forwarding any number of arguments.
I have a 2000 line jquery file, I just broke up the file into smaller ones, If I have a function in the first file, that file # 2 is referring to, it's coming up undefined.
Every file is is wrapped in a jquery ready function, What's the best way to do this?
If the function in question is declared within the scope of the ready handler, it won't be accessible to any other code, including other ready handlers.
What you need to do is define the function in the global scope:
function foo()
{
alert('foo');
}
$(document).ready(function()
{
foo();
});
P.S. A more concise way of adding a ready handler is this:
$(function()
{
foo();
});
Edit: If the contents of each of your divided ready handlers rely on the previous sections, then you can't split them up, for the reasons outlines above. What would be more sensible would be to factor out the bulk of the logic into independent functions, put these in their own files outside the ready event handler, and then call them from within the handler.
Edit: To further clarify, consider this handler:
$(function()
{
var foo = 'foo';
var bar = 'bar';
alert(foo);
alert(bar);
});
I might then split this up:
$(function()
{
var foo = 'foo';
var bar = 'bar';
});
$(function()
{
alert(foo);
alert(bar);
});
The problem with this is that foo and bar are defined in the first handler, and when they are used in the second handler, they have gone out of scope.
Any continuous flow of logic like this needs to be in the same scope (in this case, the event handler).
Function definition should not be wrapped in another function. Not unless you really want that function definition to be private. And if I understand correctly that's not your intention.
Only wrap function invocation in the jQuery ready function.
If you're worried about your functions clashing with third party function names then namespace them:
var myFunctions = {}
myFunctions.doThis = function () {}
myFunctions.doThat = function () {}
But really, you only need to worry about this if you're creating a mashup or library for others to use. On your own site YOU have control of what gets included in javascript.
Actually, for performance reasons, it may be better to keep it in one file; multiple requests actually can take up more bandwidth... but as separate files, you would need to order them in a particular order so that there is a logical sequence. Instead of having everything in a document.ready, have each script define a method, that the page will execute within its own document.ready handler, so that you can maintain that order.
Most likely the reason it's coming up undefined is because when you have separate ready calls, the scope of the code inside those calls is different.
I would reorganize my code. Any shared functions can be attached to the jQuery object directly, using $.extend. This is what we do for our application and it works well.
See this question. Hope it helps.
Everyfile shouldnt have a ready function. Only one file should have the ready function and that should be the last file.
"wrapped in a jquery ready function" is nothing else than binding stuff to the ready event that is fired when jQuery thinks the DOM is ready.
You should only bind methods that is depending on the DOM to the ready event. It doesnt matter how many binds you make, all of the methods will be executed in the binding order in the end.
Functions provide scope in JavaScript. Your code in the jquery.ready is an anonymous function, so it is unaware of the other scopes. remove the wrappings for those JavaScript functions and declare them as regular functions, a la
$(document).ready(function ()
{
functionFromFile1();
functionFromFile2();
};