Thursday, January 03, 2013

JavaScript - The Good Parts - Part 3


Introduction

This is the third post in the series of posts based on the book JavaScript - The Good Parts by Douglas Crockford. The previous post covered details of how one can create and use functions in JavaScript. This post details out the features of functions in JavaScript. JavaScript functions have features which are not present in other languages and one needs to understand these details to write better JavaScripts and to understand the JavaScript written in the JavaScript libraries.

Arguments to a Function In addition to the explicit parameters passed to a function two more parameters are passed to a function.
  1. this: The value of this variable is discussed in detail below
  2. arguments: This is an Array like variable which has the list of arguments passed to the function
The value of "this" argument The value of the "this" argument passed to a function depends on how the function was invoked.
Invoking a function through "call" function of JavaScript
A method can be executed using the "call" of JavaScript.
When a global function is invoked
When a function defined in a global scope is invoked the value of "this" is the global object.
When Function is invoked as a method
When the function is defined inside another function it is called a method. When such a function is invoked the parent function is the value of "this". All the attributes and function defined in the parent scope are accessible to this function through the "this" object.
If one has an inner function in a method then the "this" for this inner function will be the method. For the inner function to have access to the outer parent function of the method the method must expose it as its local variable. Typically this variable is called "that"
Example:
myObject.double = function () {
    var that = this; //this is myObject
    var helper = function () {
        that.value = add(that.value, that.value);
    }
    helper();
}
"this" value in function "double" will be myObject.
"this" value in function "helper" will be "double".
The function "helper" does not have direct access to myObject. It will have access to only the local variables of the function double. Unless double exposes the myObject through "that" helper method will not have access to myObject.
When function is invoked as a new
Although JavaScript provides prototype based inheritance it provides the classical way of instantiating an object using the "new" keyword. When a function is invoked using the "new" keyword the value of "this" will be the object that is created out of this invocation.
When function is invoked using the "apply" or "call" method
All functions can be invoked via apply or call method. The difference between apply and call is that apply takes an array for the list of arguments to the function being invoked and call takes individual arguments instead of an array.
Invoking using "call"
someFunction.call(thisArg[, arg1[, arg2[, ...]]]);
The thisArg1 is the object that will be passed as the value of "this" to the function someFunction
Invoking using "apply"
someFunction.apply(thisArg, [arguments]);
The thisArg1 is the object that will be passed as the value of "this" to the function someFunction
Example of using "call"
var Quo = function (string) {
    this.status = string;
}
Quo.prototype.getStatus = function () {
   return this.status;
}
var statusObject = {status: "A-OK"};
//statusObject does not inherit from Quo.prototype, but we can invoke getStatus and get the value
//of status as follows:
var status = Quo.prototype.getStatus.call(statusObject);
//the value of status will be "A-OK".
An example for apply will look the same, except that the arguments will be passed as an array.

The argument "arguments" In addition to "this" "arguments" is another variable that is available in each method. This is a list of arguments passed to the method. "arguments" is not an array in a true sense although it does behave array like. So functions that are normally available in array are not available with this variable.

Return from a function Every JavaScript function returns a value irrespective of whether there is a return statement or not. For functions that do not return anything this value is "undefined".
The only exception is when a function is invoked with a "new" does not return anything. The "this", the object created, is returned.
Pass by Reference All objects are always passed by reference. All primitive variables are passed by value.

Passing variables Passing variables is Pass by Value.
function myfunction(x){
   // x is equal to 4
   x = 5;
   // x is now equal to 5
}
var x = 4;
alert(x); // x is equal to 4
myfunction(x); 
alert(x); // x is still equal to 4


Passing Object Passing objects is pass by reference.
function myobject() {
    this.value = 5;
}
var o = new myobject();
alert(o.value); // o.value = 5
function objectchanger(fnc) {
    fnc.value = 6;
}
objectchanger(o);
alert(o.value); // o.value is now equal to 6

Passing functions Passing functions is a little more complicated. See the example below to understand how it functions, why it functions and how to actually achieve pass by reference.
function myobject() {
    this.value = 5;
}
myobject.prototype.add = function() {
    this.value++;
}
var o = new myobject();
alert(o.value); // o.value = 5
o.add();
alert(o.value); // o.value = 6
function objectchanger(fnc) {
    fnc(); // runs the function being passed in
}
objectchanger(o.add);
alert(o.value); // sorry, still just 6

Why does this happen? The problem here is the use of the "this" keyword. It"s a handy short-hand for referring to the current object context. When passing a function as a parameter, though, the context is lost. More accurately, "this" now refers to the context of the object making the call instead of the object's function we just passed in. For standalone functions, this would be the window object and for functions called from an event, this would be the event object.
So how do we achieve this?
Option 1: When you know the method If you know the method of the object that will be called then it"s fairly easy. Just pass in the object instead of the function and call that instead. Using the objectchanger from the last example you"d get the following:
function objectchanger(obj) {
    obj.add(); // runs the method of the object being passed in
}
objectchanger(o);
alert(o.value); // the value is now 7
Option 2: When you don"t know the method If you don"t know the method of the object being passed in then you need to pass both the method and the object as parameters and use the call method. call is part of the JavaScript specification and allows a function to run in the context of another object. As a result, the this keyword will reference the right object: the object we passed in.
Here"s our objectchanger function one more time:
function objectchanger(fnc, obj) {
    fnc.call(obj); // runs the method of the object being passed in
}
objectchanger(o.add, o);
alert(o.value); // the value is now 7
Note the use of the method call.

No comments: