Explore selecting/looping, arrays, functions and objects.
Sometimes a block of code should only be run under certain conditions. Flow control — via if
and else
blocks — lets you run code if certain conditions have been met.
// Flow control
var foo = true;
var bar = false;
if (bar)
{
// this code will never run
console.log("hello!");
}
if (bar)
{
// this code won't run
}
else
{
if (foo)
{
// this code will run
}
else
{
// this code would run if foo and bar were both false
}
}
While curly braces aren't strictly required around single-line if
statements, using them consistently, even when they aren't strictly required, makes for vastly more readable code.
Be mindful not to define functions with the same name multiple times within separate if/else blocks, as doing so may not have the expected result.
In order to use flow control successfully, it's important to understand which kinds of values are "truthy" and which kinds of values are "falsy." Sometimes, values that seem like they should evaluate one way actually evaluate another.
// Values that evaluate to true
"0";
"any string";
[]; // an empty array
{}; // an empty object
1; // any non-zero number
// Values that evaluate to false
""; // an empty string
NaN; // JavaScript's "not-a-number" variable
null;
undefined; // be careful -- undefined can be redefined!
In eclipse, create a new project of type 'JavaScript'. Do this by selecting Eclipse->File->New and scroll down until you see "Javascript->Project". Call the project 'js-lab-2' and accept all defaults.
Write a code fragment to do the following:
This code fragement is to be in a file called conditional.js. Then compose a simple html page which is to load this javascript file. You will need to monitor the console in google chrome.
Rather than using a series of if
/else
blocks, sometimes it can be useful to use a switch
statement instead. Switch
statements look at the value of a variable or expression, and run different blocks of code depending on the value.
// A switch statement
switch (foo)
{
case "bar":
alert("the value was bar -- yay!");
break;
case "baz":
alert("boo baz :(");
break;
default:
alert("everything else is just ok");
}
Write a code fragment containing a switch statement - call it . The switch is to check a strong called grade
for good
, excellent
and 'outstanding` strings. If should log to the console a suitable congratulatory message depending on which string is present.
Run the program by declaring and initialising the grade
variable.
Couldy you find a way to display an alert box asking for a string - and then have the switch log the message based on the value entered in the alert box?
Loops let a block of code run a certain number of times:
// A for loop
// logs "try 0", "try 1", ..., "try 4"
for ( var i = 0; i < 5; i++ )
{
console.log( "try " + i );
}
Note that in loops, the variable i is not "scoped" to the loop block even though the keyword var
is used before the variable name.
for
loopA for
loop is made up of four statements and has the following structure:
for ( [initialisation]; [conditional]; [iteration] )
{
[ loopBody ]
}
The initialisation statement is executed only once, before the loop starts. It gives you an opportunity to prepare or declare any variables.
The conditional statement is executed before each iteration, and its return value decides whether the loop is to continue. If the conditional statement evaluates to a falsey value, then the loop stops.
The iteration statement is executed at the end of each iteration and gives you an opportunity to change the state of important variables. Typically, this will involve incrementing or decrementing a counter and thus bringing the loop closer to its end.
The loopBody statement is what runs on every iteration. It can contain anything. Typically, there will be multiple statements that need to be executed, and should be wrapped in a block ( {...}).
Here's a typical for
loop:
//A typical for loop
for (var i = 0, limit = 100; i < limit; i++)
{
// This block will be executed 100 times
console.log( 'Currently at ' + i );
// Note: the last log will be "Currently at 99"
}
while
loopA while loop is similar to an if
statement, except that its body will keep executing until the condition evaluates to false.
while ( [conditional] )
{
[loopBody]
}
Here's a typical while
loop:
// A typical while loop
var i = 0;
while (i < 100)
{
// This block will be executed 100 times
console.log("Currently at " + i);
// increment i
i++;
}
Notice that the counter is incrementing within the loop's body. It's possible to combine the conditional and incrementer, like so:
// A while loop with a combined conditional and incrementer
var i = -1;
while (++i < 100)
{
// This block will be executed 100 times
console.log("Currently at " + i);
}
Notice that the counter starts at -1 and uses the prefix incrementer (++i).
do-while
loopThis is almost exactly the same as the while
loop, except for the fact that the loop's body is executed at least once before the condition is tested.
do
{
[ loopBody ]
} while ( [conditional] )
Here's a do-while
loop:
// A do-while loop
do
{
// Even though the condition evaluates to false
// this loop's body will still execute once.
alert("Hi there!");
}
while (false);
These types of loops are quite rare since only few situations require a loop that blindly executes at least once. Regardless, it's good to be aware of it.
Usually, a loop's termination will result from the conditional statement not evaluating to true, but it is possible to stop a loop in its tracks from within the loop's body with the break statement.
// Stopping a loop
for ( var i = 0; i < 10; i++)
{
if (something)
{
break;
}
}
You may also want to continue the loop without executing more of the loop's body. This is done using the continue statement.
// Skipping to the next iteration of a loop
for ( var i = 0; i < 10; i++)
{
if (something)
{
continue;
}
// The following statement will only be executed
// if the conditional 'something' has not been met
console.log("I have been reached");
}
Create and link to a file called loops.js.
In loop.js, and using lab4 as a guide:
Additionally, write code fragments to:
Finally, write a code fragment to
Arrays are zero-indexed, ordered lists of values. They are a handy way to store a set of related items of the same type (such as strings), though in reality, an array can include multiple types of items, including other arrays.
To create an array, either use the object constructor or the literal declaration, by assigning the variable a list of values after the declaration.
// A simple array with constructor
var myArray1 = new Array( "hello", "world" );
// literal declaration, the preferred way
var myArray2 = [ "hello", "world" ];
The literal declaration is generally preferred. See the Google Coding Guidelines for more information.
If the values are unknown, it is also possible to declare an empty Array, and add elements either through functions or through accessing by index:
// Creating empty arrays and adding values
var myArray = [];
// adds "hello" on index 0
myArray.push("hello");
// adds "world" on index 1
myArray.push("world");
// adds "!" on index 2
myArray[ 2 ] = "!";
'push' is a function that adds an element on the end of the array and expands the array respectively. You also can directly add items by index. Missing indices will be filled with 'undefined'.
// Leaving indices
var myArray = [];
myArray[ 0 ] = "hello";
myArray[ 1 ] = "world";
myArray[ 3 ] = "!";
console.log( myArray ); // [ "hello", "world", undefined, "!" ];
If the size of the array is unknown, 'push' is far more safe. You can both access and assign values to array items with the index.
// Accessing array items by index
var myArray = [ "hello", "world", "!" ];
console.log( myArray[2] ); // "!"
In your current project (caled js-lab-2) create a file called 'arrays.js' in the js folder. Create a HTML file to load this script.
In arrays.js do the following:
Use google chrome developers tools -> Console to verify that it works as expected. Implement one more experiment:
Observe the result.
.length
The .length
property is used to determine the amount of items in an array.
// Length of an array
var myArray = [ "hello", "world", "!" ];
console.log( myArray.length ); // 3
You will need the .length
property for looping through an array:
// For loops and arrays - a classic
var myArray = [ "hello", "world", "!" ];
for ( var i = 0; i < myArray.length; i = i + 1 )
{
console.log( myArray[i] );
}
Except when using for
/in
loops:
// For loops and arrays - alternate method
var myArray = [ "hello", "world", "!" ];
for ( var i in myArray )
{
console.log( myArray[ i ] );
}
.concat
Concatenate two or more arrays with .concat
:
// Concatenating Arrays
var myArray = [ 2, 3, 4 ];
var myOtherArray = [ 5, 6, 7 ];
// [ 2, 3, 4, 5, 6, 7 ]
var wholeArray = myArray.concat( myOtherArray );
.join
.join
creates a string representation of the array. Its parameter is a string that works as a separator between elements (default separator is a comma):
// Joining elements
var myArray = [ "hello", "world", "!" ];
console.log( myArray.join(" ") ); // "hello world !";
console.log( myArray.join() ); // "hello,world,!"
console.log( myArray.join("") ); // "helloworld!"
console.log( myArray.join("!!") ); // "hello!!world!!!";
.pop
.pop
removes the last element of an array. It is the opposite method of .push
:
// pushing and popping
var myArray = [];
myArray.push( 0 ); // [ 0 ]
myArray.push( 2 ); // [ 0 , 2 ]
myArray.push( 7 ); // [ 0 , 2 , 7 ]
myArray.pop(); // [ 0 , 2 ]
.reverse
As the name suggests, the elements of the array are in reverse order after calling this method:
// reverse
var myArray = [ "world" , "hello" ];
// [ "hello", "world" ]
myArray.reverse();
.shift
Removes the first element of an array. With .pop
and .shift
, you can recreate the method of a queue:
// queue with shift() and pop()
var myArray = [];
myArray.push( 0 ); // [ 0 ]
myArray.push( 2 ); // [ 0 , 2 ]
myArray.push( 7 ); // [ 0 , 2 , 7 ]
myArray.shift(); // [ 2 , 7 ]
.slice
Extracts a part of the array and returns that part in a new array. This method takes one parameter, which is the starting index:
// slicing
var myArray = [ 1, 2, 3, 4, 5, 6, 7, 8 ];
var newArray = myArray.slice( 3 );
console.log( myArray ); // [ 1, 2, 3, 4, 5, 6, 7, 8 ]
console.log( newArray ); // [ 4, 5, 6, 7, 8 ]
.splice
Removes a certain amount of elements and adds new ones at the given index. It takes at least 3 parameters:
// splice method
myArray.splice( index, length, values, ... );
For example:
// splice example
var myArray = [ 0, 7, 8, 5 ];
myArray.splice( 1, 2, 1, 2, 3, 4 );
console.log( myArray ); // [ 0, 1, 2, 3, 4, 5 ]
.sort
Sorts an array. It takes one parameter, which is a comparing function. If this function is not given, the array is sorted ascending:
// sorting without comparing function
var myArray = [ 3, 4, 6, 1 ];
myArray.sort(); // 1, 3, 4, 6
// sorting with comparing function
function descending( a, b )
{
return b - a;
}
var myArray = [ 3, 4, 6, 1 ];
myArray.sort( descending ); // [ 6, 4, 3, 1 ]
The return value of descending (for this example) is important. If the return value is less than zero, the index of a is before b, and if it is greater than zero it's vice-versa. If the return value is zero, the elements index is equal.
.unshift
Inserts an element at the first position of the array:
// unshift
var myArray = [];
myArray.unshift( 0 ); // [ 0 ]
myArray.unshift( 2 ); // [ 2 , 0 ]
myArray.unshift( 7 ); // [ 7 , 2 , 0 ]
.forEach
In modern browsers it is possible to traverse through arrays with a .forEach
method, where you pass a function that is called for each element in the array.
The function takes up to three arguments:
All of these are optional, but you will need at least the 'element' parameter in most cases.
// native forEach
function printElement( elem )
{
console.log( elem );
}
function printElementAndIndex( elem, index )
{
console.log( "Index " + index + ": " + elem );
}
function negateElement( elem, index, array )
{
array[ index ] = -elem;
}
myArray = [ 1, 2, 3, 4, 5 ];
// prints all elements to the console
myArray.forEach( printElement );
// prints "Index 0: 1" "Index 1: 2" "Index 2: 3" ...
myArray.forEach( printElementAndIndex );
// myArray is now [ -1, -2, -3, -4, -5 ]
myArray.forEach( negateElement );
Take the last code fragment - foreach
above, and incorporate into your arrays.js file. In Chrome, load the html page and single step through each of the methods.
Objects contain one or more key-value pairs. The key portion can be any string. The value portion can be any type of value: a number, a string, an array, a function, or even another object. When one of these values is a function, it’s called a method of the object. Otherwise, they are called properties.
As it turns out, nearly everything in JavaScript is an object — arrays, functions, numbers, even strings — and they all have properties and methods.
// Creating an object literal
var myObject =
{
sayHello : function()
{
console.log("hello");
},
myName : "Rebecca"
};
myObject.sayHello(); // "hello"
console.log(myObject.myName); // "Rebecca"
When creating object literals, note that the key portion of each key-value pair can be written as any valid JavaScript identifier, a string (wrapped in quotes), or a number:
// test
var myObject =
{
validIdentifier : 123,
"some string" : 456,
99999 : 789
};
In your current project (called js-lab-2) create a file called 'objects.js' in the js folder. Create a HTML file to load this script.
In objects.js bring in tje first code fragment in this page and run it. Then do the following:
Using this code as a guide, create a new object call myLocation
. It should have location-name, latitude, longitude and description as its fields. It should then have a method 'showLocation' to log the current location to the console
Functions contain blocks of code that need to be executed repeatedly. Functions can take zero or more arguments, and can optionally return a value.
Functions can be created in a variety of ways, two of which are shown below:
// Function Declaration
function foo()
{
/* do something */
}
// Named Function Expression
var foo = function()
{
/* do something */
}
// A simple function
var greet = function(person, greeting)
{
var text = greeting + ", " + person;
console.log(text);
};
greet("Rebecca", "Hello");
// A function that returns a value
var greet = function(person, greeting)
{
var text = greeting + ", " + person;
return text;
};
console.log(greet("Rebecca", "hello")); // "hello, Rebecca"
// A function that returns another function
var greet = function(person, greeting)
{
var text = greeting + ", " + person;
return function()
{
console.log(text);
};
};
var greeting = greet("Rebecca", "Hello");
greeting();
Create a new javascript file called 'function.js' and an associated html file to load it. Incorporate this function here into it:
// A simple function
var greet = function(person, greeting)
{
var text = greeting + ", " + person;
console.log(text);
};
greet("Rebecca", "Hello");
Introduce new function call to greet other people - and make sure the console displays the greeting. Change the greeting such that is it displayed in an alert box.
A common pattern in JavaScript is the immediately-invoked function expression. This pattern creates a function expression and then immediately executes the function. This pattern is extremely useful for cases where you want to avoid polluting the global namespace with code — no variables declared inside of the function are visible outside of it.
// An immediately-invoked function expression
(function() {
var foo = "Hello world";
})();
console.log( foo ); // undefined!
In JavaScript, functions are "first-class citizens" — they can be assigned to variables or passed to other functions as arguments. Passing functions as arguments is an extremely common idiom in jQuery.
// Passing an anonymous function as an argument
var myFn = function(fn)
{
var result = fn();
console.log(result);
};
// logs "hello world"
myFn(function()
{
return "hello world";
});
// Passing a named function as an argument
var myFn = function(fn)
{
var result = fn();
console.log(result);
};
var myOtherFn = function()
{
return "hello world";
};
"Scope" refers to the variables that are available to a piece of code at a given time. A lack of understanding of scope can lead to frustrating debugging experiences.
When a variable is declared inside of a function using the var
keyword, it is only available to code inside of that function — code outside of that function cannot access the variable. On the other hand, functions defined inside that function will have access to to the declared variable.
Furthermore, variables that are declared inside a function without the var
keyword are not local to the function — JavaScript will traverse the scope chain all the way up to the window scope to find where the variable was previously defined. If the variable wasn't previously defined, it will be defined in the global scope, which can have unexpected consequences.
// Functions have access to variables defined in the same scope
var foo = "hello";
var sayHello = function()
{
console.log(foo);
};
sayHello(); // "hello"
console.log(foo); // "hello"
// Code outside the scope in which a variable was defined does not have access
// to the variable
var sayHello = function()
{
var foo = "hello";
console.log(foo);
};
sayHello(); // hello
console.log(foo); // undefined
// Variables with the same name can exist in different scopes with different
// values
var foo = "world";
var sayHello = function()
{
var foo = "hello";
console.log(foo);
};
sayHello(); // logs "hello"
console.log(foo); // logs "world"
Take the each of the code fragements separatly, incorporate into a javascript file called 'scope.js' - and debug though to in chrome. Keep a close eye on the scope vairable pane, and confirm that the behaviour is as expected.
"Scope" refers to the variables that are available to a piece of code at a given time. A lack of understanding of scope can lead to frustrating debugging experiences.
When a variable is declared inside of a function using the var
keyword, it is only available to code inside of that function — code outside of that function cannot access the variable. On the other hand, functions defined inside that function will have access to to the declared variable.
Furthermore, variables that are declared inside a function without the var
keyword are not local to the function — JavaScript will traverse the scope chain all the way up to the window scope to find where the variable was previously defined. If the variable wasn't previously defined, it will be defined in the global scope, which can have unexpected consequences.
// Functions have access to variables defined in the same scope
var foo = "hello";
var sayHello = function()
{
console.log(foo);
};
sayHello(); // "hello"
console.log(foo); // "hello"
// Code outside the scope in which a variable was defined does not have access
// to the variable
var sayHello = function()
{
var foo = "hello";
console.log(foo);
};
sayHello(); // hello
console.log(foo); // undefined
// Variables with the same name can exist in different scopes with different
// values
var foo = "world";
var sayHello = function()
{
var foo = "hello";
console.log(foo);
};
sayHello(); // logs "hello"
console.log(foo); // logs "world"
// Functions can see changes in variable values after the function is defined
var myFunction = function()
{
var foo = "hello";
var myFn = function()
{
console.log(foo);
};
foo = "world";
return myFn;
};
var f = myFunction();
f(); // "world"
// Scope insanity
// a self-executing anonymous function
(function() {
var baz = 1;
var bim = function() {
alert( baz );
};
bar = function() {
alert( baz );
};
})();
// baz is not defined outside of the function
console.log( baz );
// bar is defined outside of the anonymous function
// because it wasn't declared with var; furthermore,
// because it was defined in the same scope as baz,
// it has access to baz even though other code
// outside of the function does not
bar();
// bim is not defined outside of the anonymous function,
// so this will result in an error
bim();