The bind function takes advantage of something in JavaScript known as a closure. This takes advantage of the way that scope works within JavaScript and can be used to hide variables within a function. For binding it is making sure that the reference to the object that created the object is tracked.
Scope simply is a term that describes which variables are known to a particular piece of code. The visibility of a variable depends on where it is declared. When declared outside of a function it is global and is visible to everything, including other scripts running on the same web page, which is why you do not want to have global variables in your code if you can help it. Variables declared within a function are only visible to code within that function. Variables declared within a loop are only visible to code within that loop, and so on. The code within the loop, however, can see all the variables in the scopes leading up to it.
The purpose of scope is two-fold. First, each layer of scope can be considered it's own name-space. Second, and more importantly, this allows JavaScript to know which variables are no longer needed and can be garbage collected so that the memory it used can be reclaimed for use by other things. There is an exception to this garbage collection rule, any variable that is still being referenced by another variable that is still in scope will not be garbage collected.
Functions within another function have access to the parent function's variables. This information is stored in a scope chain, the details of which are a bit beyond the scope of this chapter but the nutshell is that the function has access to the information contained within the function that created. This means that if we have a case where there is a reference to the inside function then even if the outside function goes out of scope, the outside functions data will not be garbage collected as it is still referenced by something that is still in-scope.
Here is a simple demonstration:
// General function for printing to the web page so results can be seen// without requiring the console
function printTestLine(s) {
// we are grabbing an html element
var results = document.getElementById("testResults");
// now append the new line to this element
results.innerHTML = results.innerHTML + "
" + s
}
function HiddingVariables(p, v) {
this.publicValue = p;
var hiddenValue = v;
this.getHiddenValue = function() {
return hiddenValue;
}
this.bindPrint = function() {
var owner = this;
return function() {
printTestLine("The hidden value is " + owner.getHiddenValue());
}
}
}
var hiddenVar = new HiddingVariables(21, 42);
printTestLine("Attempting to read public variable: " + hiddenVar.publicValue);
printTestLine("Attempting to read hidden variable: " + hiddenVar.hiddenValue);
printTestLine("Getting through method: " + hiddenVar.getHiddenValue());
setTimeout(hiddenVar.bindPrint(), 1000);
When the HiddingVariables function runs, the public value is stored as part of the object. The hidden variable is a local variable which means that it will go out of scope as soon as the function finishes running. As the function has a reference to this value, it is still visible to it. If you still don't quite understand how a closure works, don't worry too much as it is not necessary to understand them to use them.
Now that we have a basic understanding of classes we use some of these concepts in the next chapter when we create the Nightmare Maze game.
Saturday, February 23, 2019
Sunday, February 10, 2019
5.6 Binding Events
The one really nice thing about classes is that they are a nice way of grouping data with the code that manipulates that data. This leads to a very common situation in programming where you want to do something with the data when an event happens. Event-driven programming is where the behavior of a program is structured around events instead of sequences and is the way you typicality approach user-interface related tasks. Combing objects with events seems like a no-brainer but this is where JavaScript starts showing some strangeness. To understand lets write a simple event.
// General function for printing to the web page so results can be seen
// without requiring the console
function printTestLine(s) {
// we are grabbing an html element
var results = document.getElementById("testResults");
// now append the new line to this element
results.innerHTML = results.innerHTML + "
" + s
}
// The timeout method uses a callback which gets called asyncronously
function tickTest(){
printTestLine("Tick Test was called!")
}
setTimeout(tickTest, 1500);
Timing events in JavaScript can be controlled by using a timeout. SetTimeout simply takes a function and a duration in milliseconds which are thousandths of a second. After the duration has expired it will call the function. Note that the timer is not precise and that the delay can be more than the specified amount bur for general usage timeouts are adequate. Running the above would result in a second-and-a-half delay before something was printed.
Now lets write a simple class that will store a value and when a function is called will display that value back.
// A simple class that stores a value and can display the currently stored value when asked to.function Demo() {
this.setValue = function(n) {
this.storedValue = n;
}
this.displayValue = function() {
printTestLine("Stored value is " + this.storedValue);
}
this.displayWithMessage = function(s) {
var str = "NO MESSAGE PROVIDED ";
if (s !== undefined)
str = s;
printTestLine(str + this.storedValue);
}
}
// as this shows, the class works as expected
var demo = new Demo();
demo.setValue(42);
demo.displayValue();
demo.displayWithMessage("Custom message before value ");
This works just fine so using it as part of an event seems trivial but lets try that.
// however, the value does not seem to exist if used as a callback.
setTimeout(demo.displayValue, 2000);
\end{lstlisting}
}
After a couple of seconds delay we get a rather interesting result. This is a byproduct of the way JavaScript calls methods within a function and is something that we will discuss in detail next fortnight. Obviously, this means that we can't use events with classes. But, as this is an obvious necessity, there is a bind function that browsers provide to solve this problem. By simply adding the bind function to the end of the function you are calling you will be able to specify which class the event is for. The function takes the instance of a class to use as it's parameter and does not need to be the calling instance but I can't think of any situation where I wouldn't want to use the same instance.
// this is where binding comes in
setTimeout(demo.displayValue.bind(demo), 2500);
// Binding also helps with parameters.
setTimeout(demo.displayWithMessage.bind(demo), 3000);
setTimeout(demo.displayWithMessage.bind(demo, "Message added in Binding "), 3000);
As can be seen from the demo code, binding can also be used to add additional parameters to a callback function. When dealing with situations such as having multiple buttons being handled by the same logic, this is an incredibly convenient feature of the bind function. But what exactly is the bind function doing? Tune in next fortnight to find out!
// General function for printing to the web page so results can be seen
// without requiring the console
function printTestLine(s) {
// we are grabbing an html element
var results = document.getElementById("testResults");
// now append the new line to this element
results.innerHTML = results.innerHTML + "
" + s
}
// The timeout method uses a callback which gets called asyncronously
function tickTest(){
printTestLine("Tick Test was called!")
}
setTimeout(tickTest, 1500);
Timing events in JavaScript can be controlled by using a timeout. SetTimeout simply takes a function and a duration in milliseconds which are thousandths of a second. After the duration has expired it will call the function. Note that the timer is not precise and that the delay can be more than the specified amount bur for general usage timeouts are adequate. Running the above would result in a second-and-a-half delay before something was printed.
Now lets write a simple class that will store a value and when a function is called will display that value back.
// A simple class that stores a value and can display the currently stored value when asked to.function Demo() {
this.setValue = function(n) {
this.storedValue = n;
}
this.displayValue = function() {
printTestLine("Stored value is " + this.storedValue);
}
this.displayWithMessage = function(s) {
var str = "NO MESSAGE PROVIDED ";
if (s !== undefined)
str = s;
printTestLine(str + this.storedValue);
}
}
// as this shows, the class works as expected
var demo = new Demo();
demo.setValue(42);
demo.displayValue();
demo.displayWithMessage("Custom message before value ");
This works just fine so using it as part of an event seems trivial but lets try that.
// however, the value does not seem to exist if used as a callback.
setTimeout(demo.displayValue, 2000);
\end{lstlisting}
}
After a couple of seconds delay we get a rather interesting result. This is a byproduct of the way JavaScript calls methods within a function and is something that we will discuss in detail next fortnight. Obviously, this means that we can't use events with classes. But, as this is an obvious necessity, there is a bind function that browsers provide to solve this problem. By simply adding the bind function to the end of the function you are calling you will be able to specify which class the event is for. The function takes the instance of a class to use as it's parameter and does not need to be the calling instance but I can't think of any situation where I wouldn't want to use the same instance.
// this is where binding comes in
setTimeout(demo.displayValue.bind(demo), 2500);
// Binding also helps with parameters.
setTimeout(demo.displayWithMessage.bind(demo), 3000);
setTimeout(demo.displayWithMessage.bind(demo, "Message added in Binding "), 3000);
As can be seen from the demo code, binding can also be used to add additional parameters to a callback function. When dealing with situations such as having multiple buttons being handled by the same logic, this is an incredibly convenient feature of the bind function. But what exactly is the bind function doing? Tune in next fortnight to find out!
Subscribe to:
Posts (Atom)