Functions Objects and Scope
[[--Word of Advice-- Understanding this stuff is critical, spend time on it . Asking and answering questions is very likely to be helpful in clearing up doubts.--]]
[[--Word of Warning-- You will read a lot of rubbish on the internet like "Crockford says 'do this/don't do that, Crockford is right/wrong, functional/oo is better/worse than oo/functional, x/y is better/worse than y/x"
Ignore it, learn the concepts for yourself and develop your own style always with the idea "use what is best for the problem you have". You can pick sides later.--]]
Remember we described "alert" like this:
"The program consists of a statement which is a call to the function that is the value of the property named alert of the global object."
So we are going to clarify what all this means assuming by now that you are comfortable with the idea of a statement.
Basically a function is a piece of reusable code formed like this:
function doSomething(withSomeArguments) {
operateOnArguments;
return theResult; }
or to take a more concrete example
function square(x) {
return x*x; }
and you would call this function with, for example, square(6), returning 36.
See if you can put together a function (base, exponent) that returns "base" to the power "exponent" eg 2 to the 5th power, which would be 32.
Now watch this video from http://goo.gl/XT9FG and read Chapter 3 from Eloquent Javascript, http://goo.gl/4DyqX (the latter has the answer to the above power problem).
Before we go further we need to talk about
Scope
Scope refers to where variables and functions are accessible (visible), and in what context they are being executed (execution context). Basically, a variable or function can be defined in a global or local scope.
Global scope
When something is global means that it is accessible from anywhere in your code. Take this for example:
function greetVisitor () {
return alert("Howdy Doodee");
}
OR
var greeting = "Howdy Doodee";
If this is run in a browser, the function or the variable scope would be window, making it visible to everything running in the browser window.
Local scope
function greetVisitor () {
var greeting = "Howdy Doodee";
return alert (greeting); }
and now greeting is not visible outside the function (if you omitted the reserved var then it would be global which you probably don't want; if there is a global var with the same name, the function will use the local one).
All javascript code is executed in an execution context. Global code (code executed inline, normally as a js file, or html page, loads) gets executed in global execution context, and each invocation of a function has an associated execution context.
When an execution context is created a number of things happen in a defined order. First, in the execution context of a function, an "Activation" object is created.
The next step in the creation of the execution context for a function call is the creation of an arguments object, which is an array-like object with integer indexed members corresponding with the arguments passed to the function call, in order. A property of the Activation object is created with the name "arguments" and a reference to the arguments object is assigned to that property.
Next the execution context is assigned the scope. A scope consists of a list (or chain) of objects. Each function object has an internal scope property that also consists of a list (or chain) of objects. The scope that is assigned to the execution context of a function call consists of the list referred to by the scope property of the corresponding function object with the Activation object added at the front of the chain (or the top of the list).
Closures
A closure is a function together with its scope; when a function is invoked, a call object encapsulating the Arguments object for the function together with named parameters and local variables, is added to the front of the scope chain.
Ordinarily, when the function exits, the call object is removed from the scope chain even if there is a nested function, provided that the nested function is only utilized in the outer function.
However, if the nested function is the return value of the outer or the nested is stored as a property of some other object, then the call object is not removed and the nested keeps its reference to it.
This last possibility is what people usually mean when they refer to (make a song and dance about) closures; in general though, closures in Javascript are simply a result of lexical scoping (meaning that functions run in the scope in which they are defined, not the scope from which they are executed.)
With the above in mind, work your way through this bit of code and see if you can understand what is happening:
function add (x) {
return function (y) {
return x + y;
};
}
var add5 = add(5);
var no8 = add5(3);
alert(no8); // Returns 8 (paste it into jsbin to check).
When you consider the possibilities for variable name conflicts in the global scope, one idea that suggests itself is to make as many of your variables local as you can; creating your own namespaces is more advanced javascript and you can search on the web if you are interested.
OK, that takes care of the "function" bit of our alert statement.
"The program consists of a statement which is a call to the function that is the value of the property named alert of the global object..."
We have that:-
-
A program/script is a collection of communicating objects.
-
An object has a collection of properties.
-
A property has a name, a value, and a collection of attributes.
-
The value of a property is a primitive value or an object.
That's it. Things that seem like basic concepts, like functions, arrays, and regular expressions are also kinds of objects. The only objects that are distinctive are functions in that they take parameters, can hold executable code and point to scopes.
Objects have properties. Access the properties with either dot or square bracket notation. You can even delete properties.
var x = {};
x.age = 17;
x.height = 65.3;
var score = x.age * x["height"];
var z = {age: 30, color: "red", total: 3};
z.last = true;
var rat = {triple: {a:4, b:undefined, c:{4:null}}, 7: "stuff"};
delete z.age;
Every object in JavaScript has a prototype. When looking up properties,the prototype
chain is searched. You can only set the prototype when the object is created.
In newer JavaScript versions, you do this with Object.create:
var protoCircle = {x: 0, y: 0, radius: 1, color: "black"};
var c1 = Object.create(protoCircle);
c1.x = 4; c1.color = "green"; // Now c1.y === 0 and c1.radius === 1
The special keyword this has one of four different meanings.
-
If used in a global scope, it refers to the global object.
-
If used in a function called via Function.apply or Function.call, the value for this was passed in.
-
If used in a function invoked with the new operator it refers to the object being created.
-
If used in a function called normally, it refers to the object the function was called through (then the function is called a method).
So what about the "global object"?
All global variables and functions become properties of the global object. In browsers. the window object doubles as the global object, and most developers use it as such without even realizing.
http://goo.gl/galYA OO Javascript
http://goo.gl/clHFJ Core Javascript
http://goo.gl/z9TZ More about Objects
http://goo.gl/ojBl More about Objects 2
Reflections/Homework
-
What do we mean when we say a variable in a function is shadowing a top level variable?
-
A recursive function, must have some sort of an end condition. Why would we get a "out of stack space" error message if a recursive function does not have an end condition?
-
Reflect about the difference between object inheritance and class inheritance
-
What is object augmentation, and how do we do it?
-
There is a way to add a method to String, such as any new String we create will have that augmented method (this is a bit different from object augmentation). How would you do this?
-
What is the difference between an array and an object.
-
Exercises 3.1 and 3.2 from chapter 3 of Eloquent Javascript (here...)
-
Write up in your own words the connection between execution context, scope and function closures.
-
Shown below is some code which does something useful. The function 'iterateAndOperate' is the one which accomplishes something useful. The remaining code helps this function. Try to understand what the function accomplishes and solve the problems in part a, b, and c. The code can be done inside the console in Javascript, or in the web browser. Please see this comment, for hints on how you may do it inside a web page(remember, HTML has special codes for spaces and newlines).
-
Use the function iterateAndOperate to draw an image which looks like this
-
Use the function iterateAndOperate to draw a triangle which looks like this
-
In your code which invokes iterateAndOperate() without any parameters, as shown. An Exception will be thrown. Catch the Exception show an Alert to the user with a user friendly error message.
//A constant to hold the String "null". To be used in typeof checks
NULL_VAL = "null";
//A constant to hold the String "undefined". To be used in typeof checks
UNDEFINED_VAL = "undefined";
/*
* This function checks if the specified parameter is null or undefined
* @param something The specified parameter to check for a null or undefined value
* @param name The name of the parameter. This will be used in the error message
* If the value 'something' is found to be null or undefined, then this method
* will throw an Error
*/
function checkNullOrUndefined(something, name) {
if(UNDEFINED_VAL == typeof(something)) {
throw new Error(name + " cannot be undefined");
}
if(NULL_VAL == typeof(something)) {
throw new Error(name + " cannot be null");
}
}
/*
* This function accepts an array object and a function reference.
* It iterates through the array and invokes the specified function
* for every element of the array. In each invocation the current
* array element is given to the function as a parameter.
* @param arr The array
* @param func The function to invoke for every element of the array
* This method does not return any specific value.
* This method throws an Error if 'arr' is null, undefined, or is not an array
* This method throws an Error if 'func' is null, undefined, or is not a function
*/
function iterateAndOperate(arr, func) {
checkNullOrUndefined(arr, "arr");
checkNullOrUndefined(func, "func");
// Verify that arr is an array
if(!(arr instanceof Array)) {
throw new Error("arr does not seem to be an array");
}
// Verify that arr is an array
if("function" != typeof(func)) {
throw new Error("func is not a function");
}
for(var i=0; i<arr.length; i++) {
func(arr[i]);
}
}