Watch our 3-minute video to find out how you can learn JavaScript with a live instructor.
Additional Resources

Advanced Techniques

In this lesson of the JavaScript tutorial, you will learn...
  1. To use the default operator.
  2. To pass a flexible number of arguments to a function.
  3. To pass a function as an argument to another function.
  4. To create anonymous functions.
  5. Other techniques related to functions.

Beyond The Basics

You can go very far in JavaScript using only the language basics that we have seen up to now but, to really unleash JavaScript's power, we need to get more familiar with a few programming techniques.

These techniques will help your code become more expressive and more reusable at the same time. They are programming patterns that leverage some language features that may not be present in other programming languages.

If you don't have experience with any other dynamically typed or functional language, the patterns you will see here may look very strange at first. But fear not, we will provide plenty of explanations and examples to make you feel comfortable enough to read and write JavaScript code that employ what you just learned.

Optional Function Arguments

When we declare a function in JavaScript, we normally include a list of the arguments the function expects.

Code Sample: AdvancedTechniques/Demos/sumAll-1.html

---- Code Omitted ----
function sumValues(val1, val2, val3) { return val1 + val2 + val3; }
---- Code Omitted ----

But this does not guarantee that our function will always be called with 3 arguments. It's perfectly valid for someone to call our function passing more than 3 or even less than 3 arguments.

var R1 = sumValues(3, 5, 6, 2, 7);
var R2 = sumValues(12, 20);

Both calls will return surprising results (surprising from the caller's perspective.)

In the first case, since we are not expecting more than 3 arguments, the extra values, 2 and 7, will simply be ignored. It's bad because the returned value is probably not what the calling code expected.

It's even worse when we look at the second example. We are passing only 2 arguments. What happens to val3? It will have the value of undefined. This will cause the resulting sum to be NaN, which is clearly undesirable.

Let's fix our function to deal with these types of situations.

Code Sample: AdvancedTechniques/Demos/sumAll-2.html

---- Code Omitted ----
function sumValues(val1, val2, val3) { if (val1 === undefined) { val1 = 0; } if (val2 === undefined) { val2 = 0; } if (val3 === undefined) { val3 = 0; } return val1 + val2 + val3; } var R1 = sumValues(3, 5, 6, 2, 7); var R2 = sumValues(12, 20);
---- Code Omitted ----

If we run our example again, we will see that we no longer get NaN for the second function call. Instead we get 32, which is probably what the calling code expected.

We now have a pretty robust function that adds 3 numbers but it still doesn't feel all that useful. Sooner or later we will need to add four or five numbers and we don't want to be updating our function to accept a val4 then a val5 parameters. That would be a less than desirable maintenance task. Fortunately JavaScript can help us with that too.

Every function, when called, receives a hidden parameter called arguments, which is an array of all the arguments passed to the function. Back to our example, the first time we call sumValues, the arguments array will contain [3, 5, 6, 2, 7] and in the second call [12, 20].

What this means is that we can ignore the passed parameters altogether an deal only with the arguments array. Let's update our function once again.

Code Sample: AdvancedTechniques/Demos/sumAll-3.html

---- Code Omitted ----
function sumValues() { var sum = 0; for (var i = 0; i < arguments.length; i++) { sum += arguments[i]; } return sum; } var R1 = sumValues(3, 5, 6, 2, 7); var R2 = sumValues(12, 20);
---- Code Omitted ----

Note how we got rid of the parameter list and now we get the values directly from the arguments array. When we run our example now we see that the returned values are correct and precisely what was expected by the caller. We now have a function that accepts as many parameters as are thrown at it and will always return the sum of all those arguments.

Truthy and Falsy

JavaScript, as we already know, has a boolean data type, which has only two possible values: true or false. Boolean expressions also evaluate to a boolean value. But that's not the entire story.

When used in a context that expects a boolean value, any JavaScript expression can be used. See below.

var NUMBER = 0;
if (NUMBER) {
 alert('You should not see this');
}
NUMBER = 1;
if (NUMBER) {
 alert('You should be reading this');
}
var TEXT;
if (TEXT) {
 alert('You should not see this');
}
TEXT = "";
if (TEXT) {
 alert('You should not see this');
}
TEXT = "hi";
if (TEXT) {
 alert('You should be reading this');
}

In the example above we are using non-boolean expressions (NUMBER and TEXT) in the if statement and some of these expressions are being understood as true and some others false.

What is happening here is Type Coercion. JavaScript does its best to convert the given expression into the desired type (boolean.)

JavaScript resolves the following values to false.

  • false (of course)
  • null
  • undefined
  • 0 (zero)
  • NaN
  • "" (empty string)

The above values are all referred to as falsy, which is a way of saying "Not the same as false but can be interpreted like such."

Every other value, including the strings "0" and "false", will resolve to true and will be referred to as truthy. Again, "Not the same as true but can be interpreted as such."

Type coercion is the reason we have the === (triple-equal or strictly equal) comparison operator in JavaScript. The regular equality operator == applies type coercion and sometimes your comparisons will not result as expected. Look at the following sample code.

var NUM = 0;
if (NUM == "") {
 alert('Hey, I did not expect to see this.');
}
if (NUM === "") {
 alert('This will not be displayed.');
}

In the first if conditional is comparing two falsy values, and the type coercion will resolve both of them to false, causing the result of the comparison to be true, which is probably not the original intent of the code.

To detect the type difference (string vs. number) we would need to use the triple equal operator, as shown in the second if statement.

Default Operator

The boolean operators && and || also use truthy and falsy to resolve each of the operands to a boolean value.

From your previous experiences with other programming languages you may be led to believe that the result of a boolean operation is always true and false. This is not the case in JavaScript.

In JavaScript, the result of a boolean operation is the value of the operand that determined the result of the operation. Let's clarify that with an example.

var A = 0, B = NaN, C = 1, D = "hi";
var RESULT = ( A || B || C || D );
alert("Result = " + RESULT);

RESULT = ( D && C && B && A );
alert("Result = " + RESULT);

The first boolean expression ( A || B || C || D ) is evaluated from left to right until a conclusion is made. The || is the boolean OR operator, which only needs one of the operands to be true for the operation result in true. A is 0, which is falsy. The evaluation continues with the remaining operands because falsy didn't determine anything yet. When checking B, which is NaN and also falsy, we have the same situation - we need to continue evaluating. Then we check C, which is 1 and resolves to true. We no longer need to continue evaluating the remaining operands because we already know the expression will result true. But here's the catch. Instead of resulting strictly true, it will result in the truthy value that ended the evaluation, C in our case. The message displayed will be "Result = 1".

As you might already expect, the && (boolean AND operator) works in a similar fashion, but with opposite conditions. The AND operator returns false as soon as it finds an operand that is false. If for the expression ( D && C && B && A ) we follow the same sequence we did for the OR operator we will see that, D and C are both truthy so we need to keep going, then we get to B, which is falsy and causes the evaluation to stop and return B. The message displayed then is "Result = NaN".

You may be reading all this and thinking how can this be of any use. It turns out that this behavior of returning the first conclusive value can be very handy when ensuring that a variable is initialized.

Let's take another look at a function we saw back in the Form Validation lesson.

function checkLength(text, min, max){
 min = min || 1;
 max = max || 10000;

 if (text.length < min || text.length > max) {
  return false;
 }
 return true;
}

The first two lines in this function make sure that min and max always have a valid value. This allow the function to be called like checkValue("abc"). In this case the min and max parameters will both start with the undefined value.

When we reach the line min = min || 1; we are simply assigning 1 to min, ensuring it overrides the undefined. Similarly we assign 1000 to max.

If we had passed actual values for these parameters as in checkLength("abc", 2, 10) these values would be kept because they are truthy.

With this usage of the || we are effectively providing default values for these two parameters. That's why this operator, in this context, is also called the Default Operator.

The default operator replaces more verbose code like:

if (min === undefined) {
 min = 1;
}
// becomes simply
min = min || 1;

var contactInfo;
if (email) {
 contactInfo = email;
} else if (phone) {
 contactInfo = phone;
} else if (streetAddress) {
 contactInfo = streetAddress;
}
// is greatly shortened to
var contactInfo = email || phone || streetAddress;

Exercise: Applying defaults to function parameters

Duration: 15 to 25 minutes.

Here we will revisit an earlier example and use the default operator to handle optional parameters.

  1. Open AdvancedTechniques/Exercises/sumAll-defaults.html for editing.
  2. Edit the existing sumAll() to use the default operator instead of the if blocks.

Code Sample: AdvancedTechniques/Exercises/sumAll-defaults.html

<html>
<head>
 <title>Sum all numbers, default operator</title>
 <style type="text/css">
  .wc_debug
  {
   background-color:#ffc;
  }
 </style>
</head>

<body>
 <h1>Sum all numbers, default operator</h1>
 <script type="text/javascript" src="../../Libraries/DebugHelp.js" ></script>
 <script type="text/javascript">
  insertDebugPanel();
  
  // this function uses the if blocks to 
  // handle the optional parameters.
  // Change it to use the default operator instead.
  
  function sumValues(val1, val2, val3) {
   if (val1 === undefined) {
    val1 = 0;
   }
   
   if (val2 === undefined) {
    val2 = 0;
   }
   
   if (val3 === undefined) {
    val3 = 0;
   }
   
   return val1 + val2 + val3;
  }
  
  var R1 = sumValues(3, 5, 6, 2, 7);
  var R2 = sumValues(12, 20);
  
  //print the results
  debugWrite(R1);
  debugWrite(R2);
  
 </script>
 
 
</body>
</html>

Functions Passed as Arguments

In JavaScript functions are first class data types. Functions aren't just an immutable block of code that can only be invoked. In JavaScript each function we declare becomes an object, with its own properties and methods, and can also be passed around like any other object.

Let's see how we can use functions as parameters to other functions. Consider the following example.

Code Sample: AdvancedTechniques/Demos/function-arguments.html

---- Code Omitted ----
var VALUES = [5, 2, 11, -7, 1]; function sum(a, b) { return a+b; } function multiply(a, b) { return a*b; } function combineAll(list, initialValue, operation) { var runningResult = initialValue; for (var i=0; i< list.length; i++) { runningResult = operation(runningResult, list[i]); } return runningResult; } var SUM = combineAll(VALUES, 0, sum); var PRODUCT = combineAll(VALUES, 1, multiply);
---- Code Omitted ----

You may be wondering what the following function call means: var SUM = combineAll(VALUES, sum);. In this statement we are passing the function sum as the second parameter of combineAll. We are not invoking sum yet, just passing a reference to it. Note that the open and close parenthesis aren't used after sum, that should serve as a tip off that this is not a function invocation.

The line that ultimately invokes sum is runningResult = operation(initialValue, list[i]);, which received a reference to sum in the operation parameter. When operation is invoked, in reality, it is sum that is getting called, returning the sum of the two values passed in.

This is a very important technique and the combineAll function is often called reduce. Take your time to review the code and run the example until you feel comfortable with it. We will be using this capability extensively in the remaining lessons.

Anonymous Functions

Going back to our previous example, the functions sum and multiply are only referred to once, in each call to combineAll. Furthermore, if we stick to that pattern, any new combination behavior that we desire, such as concatenate the values or compute the average value, will need a new function just to be passed to combineAll. That seems like too much overhead for such a simple thing. It would also not be very interesting to have all these functions that do such simple things scattered through out the code.

Thankfully, we don't actually need to declare each of these functions. We don't even need to come up with names for them. JavaScript allow us to create functions on the spot, any time we need a function that will only be used at that spot.

The syntax is rather compact.

Syntax
function (arg1, arg2) {
 //function statements here
}

Because the functions created this way don't have names, they are aptly called anonymous functions.

Let's revisit our previous example and use anonymous functions to replace the single-use functions we declared.

Code Sample: AdvancedTechniques/Demos/anon-func-arguments.html

---- Code Omitted ----
var VALUES = [5, 2, 11, -7, 1]; function combineAll(list, initialValue, operation) { var runningResult = initialValue; for (var i=0; i< list.length; i++) { runningResult = operation(runningResult, list[i]); } return runningResult; } var SUM = combineAll(VALUES, 0, function (a, b) { return a+b; }); var PRODUCT = combineAll(VALUES, 1, function (a, b) { return a*b; });
---- Code Omitted ----

The highlighted code represent the two anonymous functions, located where previously we had sum and multiply. This coding style can understandably be harder to read, but it also avoids all that jumping around to look up what that function that you are passing by name really does. The code of that function is right there, next to the code that is using it.

Inner Functions

Since functions in JavaScript are just one more type of object, we can create a function inside another function. These are called inner functions.

The example below shows how to create and use a function inside another one.

function analyzeText(text) {
 var index = 0;

 function getNextCharacter() {
  if (index < index.legth) {
   return text.charAt(index);
  }
  return false;
 }

 var c = getNextCharacter();
 while (c) {
  alert(index + ' ---> ' + c );
  c = getNextCharacter();
 }
}
analyzeText('abcdef');

The above example is not particularly useful. We will see more important uses of inner function when we look at private members. For the time being, just notice how getNextCharacter() has access to index and text, which are scoped to the analyzeText() function.

The eval() Function

The reason we are mentioning eval() in this lesson is to acknowledge its existence and to urge you not to use it. We will explain why, but first let's explain what it does.

eval interprets a string containing JavaScript code. It can be a simple expression like "1 + 2" or a long and complex script, with functions and all.

Here's one example that is not too different from what we can find in live sites on the web.

function getProperty(objectName, propertyName) {
 var expression = objectName + "." + propertyName;
 var propertyValue = eval(expression);
 return propertyValue;
}
var PROP = "title"; //assume this was given by the user
alert(getProperty("document", PROP)); //shows the window title

This function creates a JavaScript expression by concatenating an object name, with a dot and a property name. Then it uses eval to evaluate that expression.

As we can see eval is a powerful function, but it is also potentially dangerous and incredibly inefficient. It's dangerous because it's typically used to evaluate user entered input, which not always is a safe thing to do. It's inefficient because each call to eval starts a JavaScript compiler.

The use of eval normally reveals lack of knowledge from the developer that wrote the script. For example, the sample that we just used is not necessary. Probably what happened was that the developer did not know about the [ ] accessor for properties. The same effect would be obtained with alert(window[PROP]);, with the advantage of not firing up a compiler just to retrieve the property value.

Remember this, eval is evil. Avoid it as much as you can. If you think you need it, maybe it's because you did not learn yet about an alternative way in JavaScript.

Variable Scope

Variable scope defines which parts of your code have access to a variable that you define. This typically varies depending where you declare the variable.

Variables in JavaScript are either global or function scoped. When a variable is declared outside of any function body, then it will become a global variable, meaning that any portion of your script will have access to it.

var NAME = 'my global value';
function displayName() {
 alert(NAME);
}
alert(NAME);
displayName();

The previous sample showed that the Name variable is visible inside the function displaName.

There's a catch, though. If you forget to declare the variable using the var operator before using the variable, the variable will be created as a global variable even if you are declaring it inside a function or inside a for loop declaration.

function prepare() {
 TEST = 123;
 alert(typeof TEST);
 //let's forget the "var" in the "for" declaration
 for (abc=0; abc<5; abc++) {
  //...
 }
}

alert(typeof TEST);
prepare();
alert(TEST);
alert(abc);

Function Scope

Variables declared with var inside a function will be visible only inside that function. It doesn't matter if the variable was declared inside an if block or a for block. Once declared, the variable becomes visible throughout the remainder of the function. What that means is that JavaScript doesn't have block scope.

Advanced Techniques Conclusion

Hopefully, with what you learned in this lesson you will be able to write much more robust JavaScript code and start leveraging some of the flexibility JavaScript puts at your disposal to create really powerful code.

If you still feel unsure of how these techniques work, re-read the lesson and look for alternative explanations on the web. Sometimes it helps reading a different phrasing of the same topic. It's important that you can at least read this type of code fluently in order to understand some of what we will be looking at in the upcoming lessons.

To continue to learn JavaScript go to the top of this page and click on the next lesson in this JavaScript Tutorial's Table of Contents.

Use of http://www.learn-javascript-tutorial.com (Website) implies agreement to the following:

Copyright Information

All pages and graphics on Website are the property of Webucator, Inc. unless otherwise specified.

None of the content on Website may be redistributed or reproduced in any way, shape, or form without written permission from Webucator, Inc.

No Printing or saving of pages or content on Website

This content may not be printed or saved. It is for online use only.


Linking to Website

You may link to any of the pages on Website; however, you may not include the content in a frame or iframe without written permission from Webucator, Inc.


Warranties

Website is provided without warranty of any kind. There are no guarantees that use of the site will not be subject to interruptions. All direct or indirect risk related to use of the site is borne entirely by the user. All code and explanations provided on this site are provided without warranties to correctness, performance, fitness, merchantability, and/or any other warranty (whether expressed or implied).


For individual private use only

You agree not to use this online manual to deliver or receive training. If you are delivering or attending a class that is making use of this online manual, you are in violation of our terms of service. Please report any abuse to courseware@webucator.com. If you would like to deliver or receive training using this manual, please fill out the form at http://www.webucator.com/Contact.cfm