Exam Objectives
Define the lifetime of variables; keep objects out of the global namespace; use the “this” keyword to reference an object that fired an event; scope variables locally and globally
Quick Overview of Training Materials
Programming in HTML5 with JavaScript and CSS3 - Training Guide - Chapters 3 and 6
Developing in HTML5 with JavaScript and CSS3 Jump Start - Module 4
MSDN - Namespacing in JavaScript
Memory Management (Mozilla Dev Net) - lifetime of variables
Digital Web Mag - Scope in JavaScript - covers "this"
Working with the JavaScript “this” KeywordJavaScript Closures
Closure (computer science)
MSDN - Use Cases for JavaScript Closures
Local and Global Scope
Variables that are created within a function with the var keyword are local to that function. The values in these variables are only accessible within the function, and they are destroyed when the function call ends. All other variables in JS are created in the global environment. It is generally considered bad to "pollute" the global namespace with a ton of variables and objects. It should be noted that implicitly created variables in a function (that is, a variable name on the left side of an assignment with no var) are created in the global namespace. Basic overview of variable scope here: MSDN - Variable Scope (JavaScript).Namespacing
The problem of polluting the global namespace can be avoided by using user defined namespaces, which are discussed in the training guide as well as the MSDN Magazine article. Creating a namespace is as simple as creating an object, and "variable" in the namespace are simple the values of properties of the namespace object:
var myNS = {};
myNS.variable1 = 5;
myNS.method1 = function () {};
Both references demonstrate how to wrap the namespace in a IIFE to make it "modular". This allows us to create private variables and methods (as we did with method in my post about objects). The training guide treates the namespace exactly the same as a class, so the above example would be written as:
(function () {
this.myNS = this.myNS || {};
var ns = this.myNS;
var variable1 = 5;
ns.method1 = function () {return variable1;};
})();
In this case, variable1 is private, and method1() is a public method that returns the value of variable1. The MSDN article calls this the "module pattern", and it seems to be pretty common. That article also presents several other patterns, with the final pattern looking something like this:
var myNS = {};
(function () {
var variable1 = 5;
this.method1 = function () {return variable1;};
}).apply(myNS);
Closures
A closure is a function wherein all the free variable are bound in the lexical environment (i.e. the scope). Basically what this means is there are no free variables, i.e. you don't have global variables floating around inside your function. Everything is local. A closure is created in JavaScript by using an outer function to bind the variables that are used by an inner function. This inner function is then returned and assigned to an object that exists in the scope of the other function. Confused? Yeah I didn't get it at first either...
Which looks a bit like this in the console:
We can peek at the inards by exploring the global object (in this case, window):
The MSDN Magazine article about closures includes a number of use cases, including the module design pattern that we looked at in my post about objects. Closures are very useful for creating event handlers.
Scope, apply(), call(), and 'this'
The Digital Web Magazine article about scope in JS likens scope to a neighborhood. I can't really relay the analogy here without basically plagiarizing it from that author, so I'll keep it plain. The scope of an object depends on the execution context in which it was created. For the above example, makeClosure() was executed in the global context, so it's scope is global. Nested functions will be have the scope of their parent functions added to their "scope chain". If a local variable and a global variable have the same name, the local variable will take precedence.
Execution context is a crucial concept in determining to what the 'this' keyword is referring. The article looks at four cases:
- Calling an Object's Method - 'this' is the object to whom the method belongs.
- Constructor - 'this' refers to the object that is created
- Function call - if we call a function without 'new', without an owner object, then 'this' refers to the global object
- Event handler - if defined inline, 'this' will refer to the global object. If added with JS, this refers to the object that fired the event.
We must be aware of when the context changes. If we try to assign an event handler based on an object method, any use of 'this' in that method will no longer point to the object (as was intended) but instead will point at the object firing the event:
function foo(bar) {
this.value = bar;
this.method = function () { console.log(this.value); };
}
function addHandler() {
var hand_foo = new foo("boo");
button = document.getElementById("mybutton");
button.onclick = hand_foo.method();
}
Why this doesn't work makes sense if we think about what actually gets assigned to the onclick property. If the value attribute of the button is value="this is the value attribute of the button! Whoops...", then assigning function () { console.log(this.value); } to onclick is going to pull the value attribute of the button, NOT the value property of the foo object. If the property of foo had been called "blahblah" or something equally nonsensical, then clicking the button would have just returned "undefined".
Although call() and apply() can be used to change the scope of the function, using them in our event handler won't work because we need a function assigned to the event, not the result of a function. With call() and apply(), we get the result because they execute immediately. The article presents a solution in using bind() to create a reference to the function call within a closure that maintains it's execution context, resulting in this refering to the foo object rather than the button, and thus returning the right value when the event is fired.
This is a look at the event handler:
Variable lifetime (memory management and garbage collection)
JavaScript is automatically garbage collected, so variables do not need to be explicitly freed. The Memory Management article contends, thought, that devs are not freed from thinking about memory usage. When variables are declared, JS automatically allocates memory for them. Once the program is "done" with the objects, they should be garbage collected and the memory allocated to them freed. The algorithm most modern browsers use to do this is called "mark and sweep". Basically the garbage collector starts from the root (global object) and finds everything that can be referenced from the root. Anything that is not referenced from the root is freed. So a function call that creates several objects will allocate memory for those objects, and when the function returns those objects will be unreachable, and thus subject to garbage collection. This contrasts to an earlier, naive algorith that counted references and was vulnerable to cycles. We can see this if we assign two objects to have properties pointing to each other:
If these two objects were created in a function, they would still reference each other even after the function returned and they became unreachable from the root, thus they would not be garbage collected using a reference counting algorithm.
No comments:
Post a Comment