Saturday, January 11, 2014

02 - Parameters and Arguments

The Number of Parameters and Arguments

The term parameter is used to describe the names for values that are expected to be supplied. The term argument is used for the values provided for each parameter. In many languages, the number of parameters and the number of arguments should be same. However, JavaScript behaves in a different way. The number of parameters declared in a JavaScript function signature can be different from the number of arguments passed to the function when it is invoked.

If some arguments are not passed into a function when it is invoked, the values of the missing  arguments are undefined. Let's take a look at the following sample code:
function greeting(name, greetingWord) {
    if (greetingWord === undefined) {
        greetingWord = "Hi";
    }
    
    console.log(greetingWord + ", " + name);
}

greeting("Harry", "Hello"); // Hello, Harry 
greeting("Peter"); // Hi, Peter
A function greeting with two parameters is declared firstly, then it is invoked twice. When two arguments are passed, they are concatenated and logged into the console. It's more interesting when there is only one argument "Peter" passed into the function. Arguments match parameters from left to right. If some parameters are not matched to passed arguments, they remain undefined. Therefore, the parameter name becomes "Peter", and greetingWord becomes undefined. Inside the function, a default value "Hi" is assgined when greetingWord is undefined, so "Hi, Peter" is logged to the console eventually.

When the number of arguments passed into the function when it is invoked is more than the number of parameter in the function declaration, the unmatched arguments will be discarded.
For exapmle, a function add with two parameters is declared in the following sample code, and then three arguments are passed when it is invoked:
function add(num1, num2) {
    return num1 + num2;
}

console.log(add(1, 2, 3)); // 3
Again, arguments match parameters from left to right. If some arguments don't match any parameters, they are not in use in the function. Therefore, num1 becomes 1, and num2 becomes 2 in the code above, and the returned sum is 3.

Arguments Property of Functions

Each function is an object in JavaScript, so a function can have its own properties. One of the interesting properties is arguments, which contains all the arguments passed into the function when it's invoked. Even more arguments are passed into function, all of the arguments  are accessible via the arguments property.

The following sample code shows how to use the arguments property:
function add() {
    var sum = 0;
    for(var i = 0; i < arguments.length; ++i) {
        sum += arguments[i];
    }
    
    return sum;
}

console.log(add(1, 2, 3)); // 6
A function add is declared with no explicit parameters. It is then invoked with three arguments, which are 1, 2, and 3 respectively. These argument are not acutally discarded, but remain accessible via the arguments property. The arguments property is an object, which looks like an array, and have its own property length and elements are indexed beginning from 0. Therefore, the returned sum is 6, which equals to 1+2+3.

No Overloading

Many programming languages have the overloading mechanism, which allows many functions are defined with same name but difference number of parameters. However, there is no overloading in JavaScript, because a function can be invoked with difference number of arguments. If multiple functions with the same name but different number of parameters are defined, the last function will overwrite the  preceding ones, as shown in the sample code below:
function add(num1, num2, num3) {
    return num1 + num2 + num3;
}

function add(num1, num2) {
    return num1 + num2;
}

console.log(add(1, 2, 3)); // 3
In the code above, a function named add is defined with three parameters are defined at first, and then another function also named add is defined with two parameters. The latter function overwrite the former one. When the function add is invoked with three arguments, only two arguments passed into the second function and the last argument 3 is not in use, so the returned value is 3 which is 1 plus 2.

Property arguments.callee

The arguments property of functions is an object, and it has its own properties. Besides arguments.length, another interesting property is arguments.callee, which points to the container function of arguments.

The property arguments.callee is widely used in recursive function. Recursive functions call themselves, usually through function names. There are anonymous functions in JavaScript, which don't have names. How to define recursive functions when they are anonymous? A solution is to name them explicitly, and the other one is to utilize arguments.callee. Let's have a look at the following sample code:
var threeToPowerOfTen = (function (exponent) {
    if (exponent === 1) {
        return 3;
    }
    return 3 * arguments.callee(exponent - 1);
})(10);

console.log(threeToPowerOfTen); // 59049 
In order to get the value of 3 to power of 10, an anonymous function is defined which is also recursive. Since an anonymous function doesn't have a name, it can't be recursively invoked via its name. In the code above, the property arguments.caller is utilized to invoke the anonymous function. That's to say, the property arguments.callee makes it possible to define recursive anonymous functions.

The property arguments.callee has its own value even a recursive function has a name. When a function is recursively invoked via its name, the recursive functionality couples with its name. The function name is just a variable name, and it can be assigned to other functions, or even other object. When the function is assign another meaning, the recursive functionality breaks. For example:
function power(base, exponent) {
    if (exponent == 1) {
        return base
    }

    return base * power(base, exponent - 1);
}

var realPower = power;

power = "More power, more powerful";

console.log(realPower(2, 3));

// Result:
// TypeError: string is not a function
A recursive function power is defined in the code above. It's recursively invoked via its name power inside the function. Following the function declaration, the function is assigned to another variable name realPower, and then power is assigned to a string. Finally the function is invoked which has can be referenced with the name realPower, but its functionality breaks. Inside the function, the it recursively invokes itself via the name power, but power now is not a function anymore. Therefore, an error is thrown complaining that a string is not a function.

In order to decouple the recursive functionality and the function name, the function can be replaced with argument.callee which also points to the function. Therefore, the recursive function can be revised into the following code:
function power(base, exponent) {
    if (exponent == 1) {
        return base
    }

    return base * arguments.callee(base, exponent - 1);
}

var realPower = power;

power = "More power, more powerful";

console.log(realPower(2, 3)); // 8
In the revised code, the function recursively call itself via arguments.call. Therefore, even when the name power is assigned to a string, the recursive functionality remains.

No comments :

Post a Comment