Crockford defines "Closure" as follows:
The context of an inner function includes the scope of the outer function.Crockford presents the following example that includes a global variable, which I renamed slightly and ran in the Google Chrome console:
An inner function enjoys that context even after the parent functions have returned.
var names= ['zero','one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];I am using the JavaScript "typeof" operator to inspect what is being generated. Crockford make the point that this function is making use of a global variable, and that global variables are evil, which I guess I go along with; although I wonder if this example isn't already demonstrating closure, i.e. the global variables are just variables in the global function scope that the local function is getting access to, but perhaps that's not how global scope works in JavaScript, but anyway ... before moving on, let's think how we would so the same thing in Java:
var global_digit_name = function (n) {
return names[n];
};
global_digit_name(3);
"three"
typeof global_digit_name
"function"
static String[] names = {"zero","one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};Here I am using static scopes to create a "global" variable and a "global" method. I can't assign the method to a variable in Java, so I just call it in the main function. Anyhow, back to the JavaScript example, in the name of removing the evil global variable Crockford the presents a slow alternative:
static String global_digit_name(int n) {
return names[n];
}
public static void main(String[] args) {
System.out.println(global_digit_name(3));
}
var slow_digit_name= function (n) {So the JavaScript advantage here is that the names variable is safely inside the function and can't interfere with other global variables. The Java equivalent is straightforward:
var slow_names= ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];
return slow_names[n];
};
slow_digit_name(3);
"three"
typeof slow_digit_name
"function"
slow_names
ReferenceError: slow_names is not defined
static String slow_global_digit_name(int n) {In both Java and JavaScript cases the slowness results from reallocating the array each time the method is called which is a bit wasteful. Now comes the JavaScript example that Crockford labels "closure":
String[] slow_names = {"zero","one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
return slow_names[n];
}
public static void main(String[] args) {
System.out.println(slow_global_digit_name(3));
}
var closure_digit_name= (function () {So again we avoided the nasty global variable, but now the object literal closure_names is not allocated each time. Interestingly I can get definitions of the functions defined so far from the Chrome console:
var closure_names= ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];
return function (n) {
return closure_names[n];
};
}());
closure_names
ReferenceError: closure_names is not defined
typeof closure_digit_name
"function"
closure_digit_nameWhich allows us to see clearly that closure_digit_name is not reallocating the object literal each time. Where I am perhaps confusing myself is that I thought by assigning the return of a call to a function to closure_digit_name that we were turning closure_digit_name into an object, but not according to the Google Chrome console which still gives the type of closure_digit_name as a function. I think it is me just confusing myself. Crockford doesn't mention objects being returned, here's what he says (with my minor edit to make it apply to my example - unfortunately I don't have his colour highlighting):
function (n) {
return closure_names[n];
}
slow_digit_name
function (n) {
var slow_names= ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];
return slow_names[n];
}
We assign the return value of the outer function to closure_digit_name. The outer function has now returned, digit_name now contains a function, which is the green function. That green function still has access to names, even though names is a private variable of a function that's already returned. That's closure: one function closes over the variables of another function. This turns out to be one of the most important features in JavaScript; this is the thing that it got amazingly right. This is the thing that makes JavaScript one of the world's brilliant programming languages.In my own words it seems like we are defining a function on the fly that happens to have a variable and a function, so the following longer version should be exactly equivalent:
var my_function = function () {which Google Chrome confirms, and gives me the advantage of being able to check the contents of my_function:
var closure_names= ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];
return function (n) {
return closure_names[n];
};
}
var closure_digit_name= my_function();
my_functionWhich reassures me slightly as at least I can now see where closure_names is, sort of :-) It really does seem to me to say that by calling my_function() that we get back some sort of object with a function and a variable, but I guess that's part of the JavaScript power, that functions can have other functions and variables, but it does seem slightly odd that the consequence of calling a function is to return a function. Quoting from Crockford's book:
function () {
var closure_names= ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];
return function (n) {
return closure_names[n];
};
}
A function always returns a value. If the return value is not specified, then undefined is returned. If the function was invoked with the new prefix and the return value is not an object, then this (new object) is returned instead.According to which, I would have thought that my_function as I define it above, should return "undefined" and not the function that it contains, but that is what seems to happen. On closer inspection I see that my_function is not just containing the sub-function, it is returning the sub-function, so everything starts to make sense, and the example can be unobfuscated further like so:
var my_function = function () {and Google Chrome confirms for me that this does exactly the same thing, and as far as I know there is no simple equivalent in Java. Interestingly I can't access the closure_names variable from the reference I now have to my_function:
var closure_names= ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];
var my_sub_function = function (n) {
return closure_names[n];
};
return my_sub_function;
}
var closure_digit_name= my_function();
my_function.closure_namesAnd I guess that is because this really is a function and not an object ..., but it certainly seems to have an object like nature, at least the way I think about them :-)
undefined
No comments:
Post a Comment