Let's say I'm creating a chat system in javascript.
var ChatController = function() {
this.receiveMessageInteractor = new ReceiveMessageInteractor(this);
// ReceiveMessageInteractor delegate
this.didReceiveMessage = function(message) {
// ...
};
};
The ChatController also does some other stuff related to creating the html for the messages, but that's not important here.
The ChatController sets himself as a delegate of the ReceiveMessageInteractor, which will call the didReceiveMessage when a new message arrives.
var ReceiveMessageInteractor = function(delegate) {
this.observer = NotificationCenter.addObserver('DidReceiveMessageNotification' , function(data) {
var message = data['message'];
// format some message data
delegate.didReceiveMessage(message)
});
};
The ReceiveMessageInteractor just subscribes to a notification (NotificationCenter here is similar to the iOS one), does some formatting with the data and passes a message object to the delegate;
When the chat view goes of the screen (html gets deleted), my MenuController stops holding a pointer to ChatController, in which case I'd like it to be deleted, along with ReceiveMessageInteractor and observer.
The problem is that Javascript has no weak references, so ReceiveMessageInteractor is holding a pointer to ChatController, and even if ChatController wasn't holding a pointer to ReceiveMessageInteractor, my ChatController would still be alive, because the notification callback is holding a pointer to it (delegate).
So even if ReceiveMessageInteractor stopped existing, my ChatController would still not go away when the MenuController stops holding a pointer to it (because I can't have a weak reference inside the notification callback).
How do I solve this problem?
How do I solve this problem?
By understanding JavaScript. The problem is not that "Javascript has no weak references", the problem is that you don't know how to work without them because you come from a language that has them.
How would you remove that reference in any other language that doesn't have weak refs natively? Let's say C++. You would do like everyone does, including the implementors of the compiler/garbage collector/weak refs you're used to: you clean up after yourself.
function ChatController() {
this.receiveMessageInteractor = new ReceiveMessageInteractor(this);
// ReceiveMessageInteractor delegate
this.didReceiveMessage = function didReceiveMessage(message) {
// ...
};
this.destroy = function destroy() {
this.receiveMessageInteractor.destroy();
};
};
function ReceiveMessageInteractor(delegate) {
function callback(data) {
var message = data.message;
// format some message data
delegate.didReceiveMessage(message);
}
this.observer = NotificationCenter.addObserver('DidReceiveMessageNotification', callback);
this.destroy = function destroy() {
// Or however you NotificationCenter works, I don't know
NotificationCenter.removeObserver('DidReceiveMessageNotification', callback);
};
};
The Observer pattern implies resource management, even though it's not obvious (how is an "observation" relationship a resource??). Acquire and release. No hand-holding.
Also, notice the change in style. And please, learn the language, use prototypes, and, although not everyone will agree with me on this point, do not assign methods in the constructor.
edit: I forgot to add: ReceiveMessageInteractor? Really? What's wrong with MessageReceiver or something in that vein?
Your problem is not with the absence of weak references. All of your objects continue to have a hard reference, all originating from your NotificationCenter.
The NotificationCenter has a reference to the data handler, which has closure access to it's parent ReceiveMessageInteractor instance, as well as access to the delegate variable. Removing a reference to delegate from elsewhere won't break the anonymous function's access to it, therefore it stays.
What you'll need to do is add a .cleanup() method to each Controller that is called when it is removed.
In the ChatController.cleanup() method, you would want to call a method to remove the observer, something along the lines of this.receiveMessageInteractor.observer.unsubscribe().
The .unsubscribe() method should be defined in the NotificationCenter and remove the function(data) { ... } method you defined in .addObserver() from whatever data structure is holding it (or further down the line).
This is the same kind of pattern Facebook utilized in it's React framework + Flux architecture. Each component has a componentWillUnmount() method that assists in cleaning up data event handlers just like yours.
Related
I'm interested in being able to determine whether or not a class method is being executed within the class itself or from outside, and having different functionality based that.
What's a way of determining whether or not a class method is being executed within the class itself?
This is a quick and dirty example of what I'm interested in:
class Example {
internal = false
meow () {
console.log(this.internal);
return 'meow'
}
makeSound () {
return this.meow.bind({...this, internal: true})();
}
}
const example = new Example();
console.log(example.meow())
console.log(example.makeSound())
Here, Example#meow runs two times, one from within Example#makeSound and once on it's own. I would like a way of knowing from within Example#meow when the method has been executed from within the class, versus from outside the class, as in example.meow().
One way to do this, is with binding all internal calls to methods, if I add a property internal true, to this bound copy of this, then I can check from within Example#meow where the call is coming from.
This is only a proof of concept, but it's not great to try and clone this, and seems to be a real pain.
I was facing a problem with callbacks in Javascript. I solved my problem using what I would call an ugly property of Javascript (so to say, something that would logically be forbiden and never work in other languages than Javascript). So my question: Is there an ELEGANT way, to do the same thing.
I will so begin with the beginning. My goal was to wrap, in some manner, the Web Audio API. In the architecture, I implemented a class, lets call it AudioRessource, which is destined to be an interface (abstraction) in some manner of the AudioBuffer object of the Web Audio API.
This class (AudioRessource) have a prototype member function that must simply take an url as argument to automatically load audio data, decode it, handle errors, etc and finally hold the resulting AudioBuffer object in a "pseudo-private" member:
function AudioRessource()
{
this._aBuffer = null; // future reference to `AudioBuffer` object
this._loadStatus = 2;
};
AudioRessource.prototype.loadData = function(url) {
/* deal here with async functions to
provides audio data loading automation */
}
The main problem here, is that this will be an object instance (of AudioRessource) which will create the callback functions, using only local references, and must be able to pass the final AudioBuffer object to itself.
To load the raw audio data, this is pretty simple, I use the XMLHttpRequest object, with an extra property set as member of the XMLHttpRequest object, like this:
AudioRessource.prototype.loadData = function(url) {
let req = new XMLHttpRequest();
req.extraProperty = this; // reference to `AudioRessource` instance
req.onload = function(){
// retrive instance reference within the callback
this.extraProperty._loadStatus = 0;
}
req.onerror = function(){
// retrive instance reference within the callback
this.extraProperty._loadStatus = -1;
}
req.open('GET', url, true);
req.send(null);
this._loadStatus = 1;
}
The big problem appear when we have to decode the coded raw audio data into PCM data, that is, an Web Audio API AudioBuffer object instance. Indeed, the Web Audio API provides only one function to achieve this, and this function is asynchronous, and takes a callback that simply recieve the resulting buffer as argument: how to "catch" this resulting buffer to assign it to the proper AudioRessource instance (the one who lauched the process) ? This work that way:
AudioCtx.decodeAudioData(rawData,
function(result){
// do something with result },
function(error){
// do something with error });
My first naive approach, was to think like we were in C/C++ : I simply put an AudioRessource instance function "pointer" (reference) as callback, this way, the AudioRessource instance will directly recieve the buffer:
// where 'this' is an `AudioRessource` instance
AudioCtx.decodeAudioData(rawData,
this._handleDecodeSuccess,
this._handleDecodeError);
However, this does not work, because in javascript, this is not a "function pointer" that is passed into the decodeAudioData, but if I well undstand, an literal expression, that is, the "ASCII content" of the function... So the 'this' reference is lost !
I spent some time to try understand how this kind of asynchronous function is attended to work, since to me, coming from C/C++, this is simply an heresy: The function does not take any extra argument, no way to pass any external reference... "What is that thing ?". Then I finaly decided to try the "Illogical Javascript logic" way... And I found the solution :
// Create local variable which stores reference to 'this'
let thisInstReference = this;
// Use the local variable to write our callback
AudioCtx.decodeAudioData(rawData,
function(resut){
thisInstReference._aBuffer = result;
thisInstReference._loadStatus = 0;
},
function(resut){
thisInstReference._loadStatus = -3;
});
To be honnest, to me, this is simply freaking. First of all, I even don't understand what realy happen: HOW a local variable (to a object instance's member function), that stores a reference to an object instance (this), can be used "as this" in a callback function ? I do not even understand how a language can allow this kind of thing. Secondly, to me, this not a "proper way" to code something: this code is simply illogical, dirty, this works but this appear as an ugly hack that takes advantage of Javascript misdesign.
So here is my question: How to achieve this, in a elegant way ?
Your problem is simply due the the nature of how this works in javascript. The value of this is not bound at compile time nor at runtime but instead very late at call time.
In the following code:
AudioCtx.decodeAudioData(rawData,
this._handleDecodeSuccess,
this._handleDecodeError);
.. the value of this inside _handleDecodeSuccess and _handleDecodeError is not determined at object creation time but instead at the time they are called. And it is the decodeAudioData method that will eventually call them when decoding is complete. This causes the value of this to become something else (depending on how the functions are called).
The modern solution is to statically bind this to the functions:
AudioCtx.decodeAudioData(rawData,
this._handleDecodeSuccess.bind(this),
this._handleDecodeError.bind(this));
Note: the .bind() method creates a new function that wraps your function with this permanently bound to the argument you pass to it.
The traditional solution is to capture this inside a closure like what you have done.
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 have tried searching through a lot of S.O. pages but nothing has touched EXACTLY on this top while also NOT USING JQUERY.... I am trying to stick to pure JavaScript as I want to learn it 115% before advancing my current knowledge of JQuery.
I have an object called ScreenResizeTool like this...
function ScreenResizeTool(currImg) {
window.addEventHandler('resize', function() {
listen(currImg);
}, true);
}
and a method like this...
ScreenResizeTool.prototype.listen = function(currImg) {
//Random Code For Resizing
};
My trouble is probably obvious to an experienced JavaScript user but I am having trouble not making this into a messy dirty awful OOP set. I have done various tests to show and prove to myself that the this inside the addEventHandler changes when it becomes bound to the window. This much I assumed before testing but I was able to see that once window.resize event happens the listen method is gone and not a part of the global window variable....
I have also tried adding a this capture such as this.me = this inside the object constructor however it also couldn't see the me variable once it ran. Once the window took the function over it no longer knew anything about the me variable or any reference to my class methods....
I am aware that I could separate this differently but my goal here is to learn how to fully encapsulate and use as many clean OOP structures as possible as I just came from the .NET world and I need it in my life.
I am also aware that I could make messy calls and or store this object or access to the methods inside the window variable but that seems outright wrong to me. I should be able to fully encapsulate this object and have its events and methods all implemented in this class structure.
I also know that the currImg variable is not going to be seen either but lets start small here. I assume once I figure out my incorrect train of thought on scope for JavaScript I should be fine to figure out the currImg problem.
I know there's 1000 JavaScript programmers out there waiting to rip me a new one over asking this simple question but I gotta know...
Thoughts anyone?
this inside a function bound to a DOM Object (like window) will always refer to that object.
this inside a constructor function will always refer to the prototype.
A common practice to circumvent the this issue, as you mentioned, is to cache it in a variable, often called self. Now you want the variables and properties of your object available after instantiation, so what you need is the return keyword, more specifically to return the parent object itself. Let's put that together:
function ScreenResizeTool() {
var self = this;
// method to instantiate the code is often stored in init property
this.init = function() {
window.addEventListener('resize', function() {
self.listen(); // self will refer to the prototype, not the window!
}, true);
};
return this;
}
ScreenResizeTool.prototype.listen = function() { // Dummy function
var h = window.innerHeight, w = window.innerWidth;
console.log('Resized to ' + w + ' x ' + h + '!');
};
Pretty easy huh? So we have our prototype now, but prototypes can't do anything if there's not an instance. So we create an instance of ScreenResizeTool and instantiate it with its init method:
var tool = new ScreenResizeTool();
tool.init();
// every time you resize the window now, a result will be logged!
You could also simply store the listen & init methods as private functions inside your constructor, and return them in an anonymous object:
function ScreenResizeTool() {
var listen = function() { ... };
var init = function() { ... };
// in this.init you can now simply call listen() instead of this.listen()
return {
listen: listen,
init: init
}
}
Check out the fiddle and make sure to open your console. Note that in this case I'd rather use the first function than the second (it does exactly the same) because prototypes are only useful if you have multiple instances or subclasses
The whole concept of this in JavaScript is a nightmare for beginners and in my code I usually try to avoid it as it gets confusing fast and makes code unreadable (IMHO). Also, many people new to JavaScript but experienced in object-oriented programming languages try to get into the whole this and prototype stuff directly though the don't actually need to (google JS patterns like IIFE for example as alternatives).
So looking at your original code:
function ScreenResizeTool(currImg) {
window.addEventHandler('resize', function() {
listen(currImg); // global function listen?
}, true);
}
ScreenResizeTool.prototype.listen = function(currImg) {
//Random Code For Resizing
};
First off, you probably mean addEventListener instead. In its callback you refer to listen but as a global variable which would look for it as window.listen - which doesn't exit. So you could think to do this:
function ScreenResizeTool(currImg) {
window.addEventHandler('resize', function() {
this.listen(currImg); // what's this?
}, true);
}
As you want to use the prototype.listen function of ScreenResizeTool. But this won't work either as the event listener's callback function is called with a different this and not the this that is your function scope.
This is where something comes in which makes most programmers cringe, you have to cache this, examples from code I've seen:
var _this = this;
var that = this;
var _self = this;
Let's just use the latter to be able to refer to the function within the event callback:
function ScreenResizeTool(currImg) {
var _self = this;
window.addEventListener('resize', function() {
_self.listen();
}, true);
}
Now this will actually work and do what you want to achieve: invoke the prototype.listen function of ScreenResizeTool.
See this JSFiddle for a working example: http://jsfiddle.net/KNw6R/ (check the console for output)
As a last word, this problem did not have anything to do with using jQuery or not. It's a general problem of JS. And especially when having to deal with different browser implementations you should be using jQuery (or another such library) to make your own code clean and neat and not fiddle around with multiple if statements to find out what feature is supported in what way.
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.