Arrays

Arrays are containers that can hold primitive and reference values. Arrays are ordered, that is, when a value is inserted into an array it is added at a specific position in the array.  The value will remain at that position until it is explicitly moved.  A value stored in an array is referred to as an element of the array and the position of an element in an array is referred to as the index of the element in the array.

Indices are non-negative numbers (0, 1, 2, …).  If an array has n elements in the array, valid indices for the elements in the array are {0, 1, 2, …, n-1}.

Initializing and Accessing Arrays

Arrays can be created using the Array constructor or an array literal.

var addr1 = new Array();
var addr2 = new Array(4);     // length is set to 4
var addr3 = new Array('123 Main St', 'Dallas', 'TX', 12345);   // mixing types is ok
var addr4 = Array(4);         // omitting the new keyword is ok

var addr6 = [];               // creates an empty array
var addr5 = ['123 Main St', 'Dallas', 'TX', 12345];   

Elements can be added and retrieved at a specific index using brackets []. In the example below the array is initialized with one element (‘yellow’) in it.  The second statement overwrites the element at index 0 with the string ‘blue’.  The third and fourth lines add two more elements to the array. The last statement retrieves the element at index 0 and prints it to the console.

var colors = ['yellow'];
colors[0] = 'blue';
color[1] = 'green';
color[2] = 'red';
console.log(colors[0]);   // prints 'blue'

The Length Property

We can get the length of an array using the length property.

console.log(colors.length);   // prints 3

You can truncate the array by setting the length property.

colors.length = 2;      // truncates the array
console.log(colors.length);   // prints 2

The length property can be used as the index to add an element to the end of the array.

colors[colors.length] = 'yellow';   // element added to index 2
console.log(colors.length);         // prints 3

Elements can also be added at indices beyond the length of the array.  In this case, elements of type Undefined are added at the indices containing no elements.

colors[4] = 'orange'; 
console.log(colors[3]);  // prints 'undefined'

Named Indices

Just like objects, arrays are sets of key/value pairs.  We’ve seen earlier that we can change the value and get the value of an element at a particular index using brackets and an integer index.  Similarly, when we add elements to an array via a push(), unshift(), and splice() the elements are added at locations with integer indices.

We can also use text labels as indices in arrays.  For example, we can store the value ‘George’ with an index of name.  These named indices do not change the value of the array’s length property.  They are also not included in the string returned by toString() and are not iterated over in a for-of loop.  They are, however, iterated over in a for-in loop.

var list = [10,20,30]; 
console.log("length: " + list.length);    // 3 
list[name] = 'George'; 
console.log("length: " + list.length);    // 3 
list[list.length] = 'red'; 
console.log("length: " + list.length);    // 4 

console.log(list.toString());             // prints '10,20,30,red' - does not include 'George'

Looping Through Arrays

For-of loops can be used to loop through all of the values of an array that have integer indices.  Named indices are not included.

for (var value of list) {
    console.log(value);    // prints 10,20,30,'red' - does not include 'George'
}

For-in loops can be used to loop through the indices of an array, including named indices.  Named indices however can not be printed.

for (var index in list) {
    console.log(index);        // prints '0','1','2','3'," " - the named index is not printed, but is usable
}

for (var index in list) {
    console.log(list[index]);  // prints 10,20,30,'red','George' - notice the order
}

Detecting Arrays

To determine if an object is an Array we can use the static isArray() method of the Array class.

if (Array.isArray(colors)) {
    // do something
}

Converting Arrays to Strings

All objects have the following methods:toString() and valueOf(). Both return the same value for arrays: a comma separated string that contains the string equivalents of each element in the array.

console.log(colors.toString());  // prints 'blue,green,yellow,,orange'

The array’s join() method takes a character as an argument and returns a string containing the string representation of the elements in the array separated by the character in the parameter.

var colors = ['red','blue','green'];
var str = colors.join('|');             // “red|blue|green”

Array Methods

There are many methods that allow you to manipulate and utilize arrays.  Some of these include:

Stack Methods

The push() method adds the arguments at the end of the array and pop() removes the last element of the array.

var names = ['alice','bob'];
names.push('cece');
var last = names.pop();
console.log(last);       // prints 'cece'

Alternate Stack Methods

The shift() method removes the first element in the array.

The method unshift() adds the arguments to the beginning of the array.

var names = ['alice','bob'];
var first = names.shift();
console.log(first);               // prints 'alice'

names.unshift(first);
console.log(names[0]);            // prints 'alice'

Reordering Methods

The reverse() method reverses the order of the elements in the array.

var names = ['alice','bob'];
names.reverse(); 
for (var elm of names) {      
    console.log(elm); // prints 'bob then 'alice' 
}

The sort() method by default converts all elements to strings for comparison.

var values = [0, 1, 5, 10, 15];
values.sort();                  // 0, 1, 10, 15, 5 - Oops! Not in numeric order

The sort() method can also accept a comparison function as an argument. The comparison function must take two arguments and return a negative number if the first element is less than the second, 0 if they are equal, and a positive number if the first number is greater than the second.

To sort an array of integers we can use the following comparison function and sort.

function compare(v1, v2) {
    if (v1 < v2)
        return -1;
    else if (v1 == v2)
        return 0;
    else
        return 1;
}

var values = [10, 1, 15, 5, 1];
values.sort(compare); 
console.log(values.toString());    // prints '1,1,5,10,15'

A more efficient compare function for integers is as follows.

function compare(v1, v2) {
    return v2 – v1;
}

New Arrays From Old

The concat() method creates a copy of the existing array then adds the elements that are passed to the function to the end of the new array.

var arr1 = [1, 2, 3];
var arr2 = arr1.concat(4, 5, 6);    // 1, 2, 3, 4, 5, 6
var arr3 = arr2.concat(arr1);       // 1, 2, 3, 4, 5, 6, 1, 2, 3 – concat elms in an array

The slice() method returns a subarray. The method takes a starting index and an optional ending index that specify where to splice. The element at the starting index is included, but the element at the ending index is not included.  The original array is not modified by slice().

var arr1 = [1, 2, 3, 4, 5];
var arr2 = arr1.slice(1,4);    console.log(arr2.toString());     // prints '2,3,4'

The splice() method takes an index in the array as the first argument and allows you to delete 0 or more (second argument) elements in the array starting at the index.  The method optionally accepts additional elements that are inserted starting at the index.  The method modifies the array on which it is called and returns an array of elements that were deleted.

var arr1 = [1,2,3,4];
var arr2 = arr1.splice(1,2,'foo','bar','whoo','car'); 
console.log(arr1.toString());   // prints '1,2,foo,bar,whoo,car' 
console.log(arr2.toString());   // prints '2,3'

Location Methods

The indexOf() and lastIndexOf() methods search the array for an element identical (using ===) to the element passed in as the first argument. An optional second argument specifies the index where the search starts.indexOf() searches from front of the array to the back whereas lastIndexOf() searches from the back to the front.  Both methods return the index of the first element found or -1 if the element is not found.

Caution must be taken when searching for reference objects.

var person = {name: “Nicholas”};
var arr1 = [person];
console.log(arr1.indexOf(person));    // 0: found at index 0 
var arr2 = [{name: “Nicholas”}];      // an array containing an unnamed object - not person 
console.log(arr2.indexOf(person));    // -1: not found

Iterative Methods

The every() and some() methods each take a function that returns a Boolean.  When every() or some() are called, the function argument is called once for each element in the array.  Each time it is called, a different element of the array is passed into the function argument.  The function argument must return a Boolean indicating whether or not the element that is passed into it has some property.

The every() and some() methods also return a Boolean.  In the case of every(), if each time the function argument is applied to the elements of the array the function argument returns true, then every() will return true.

In the case of some(), if when the function argument is applied to the elements of the array, the function argument returns true at least once, then some() will return true.

The function that is passed in as a parameter to every() and some() should have 3 parameters: item, index and array.  When the function is called, the array element is passed in as the first argument, the index of the array element is passed in as the second argument, and a reference to the array is passed in as the third parameter.

var f = function(item, index, array) { 
    return (item > 2); 
};

var numbers = [1,2,3];
var result = numbers.every(f);
console.log(result);            // false – not every element is greater than 2

var numbers = [1,2,3];
var result = numbers.some(f); 
console.log(result);            // true – some elements are greater than 2

The filter() method returns a new array and has a function parameter.  The function argument must return a Boolean to indicate whether or not the array element that was passed into the function argument is to be included in the new array.

var g = function(item, index, array) {          
    return item % 2 === 0;  
};  

var numbers = [1,2,3,4];  
var newArray = numbers.filter(g);  
console.log(newArray.toString());    // prints '2,4'

The map() method also returns a new array. The function passed into the map() method returns an element which is included in the new array. Thus, one element is put in the new array for each element in the original array.

var h = function(item, index, array) {     
    if (item % 2 === 0) {         
        return item;     
    } else {         
        return 0;
    } 
}; 

var numbers = [1,2,3,4];
var newArray = numbers.map(h); 
console.log(newArray.toString());    // prints '0,2,0,4'

The forEach() method has no return type. It iterates over the elements of the array, like a for-loop.

var init = function(item, index, array) {     
    array[index] = '0' 
}; 

var numbers = [1,2,3,4]; 
numbers.forEach(init); 
console.log(numbers.toString());     // prints '0,0,0,0'

Reduction Methods

The reduce() and reduceRight() methods return a value that is built up by iterating a function (passed in as an argument) over the elements in the array. The reduce() method iterates over the elements from the start to the end, the reduceRight() method works in the opposite direction.

The function argument passed into reduce() and reduceRight() requires 4 parameters: prev, cur, index, and array. The first parameter holds the value that the function returned on the previous iteration, except the first time the function is executed, in which case the previous value is the value of the element stored at index 0 in the array.  The second parameter holds the value of the element at array[index].  The third parameter holds the index of the current element.  The last parameter is a reference to the array.

The reduce() method starts with index equal to 1.  reduceRight() starts with index equal to array.length – 2.

var add = function(prev, cur, index, array) {     
    return prev + cur; 
} 

var numbers = [1,2,3,4];
var sum = numbers.reduce(add);
console.log(sum);   // prints 10

Objects

JavaScript Objects are containers that hold key/value pairs. The keys (called properties) are strings and the values can be of any type.

We can create an object either by using the Object constructor or by assigning an object literal.

var obj1 = new Object();  
var obj2 = {};

When we define an object literal we can include in it a set of properties and their associated values. Each property is given a name followed by a colon and the value associated with the property.  If multiple properties are defined in the object literal, they are separated by commas.   The name of a property is given without quotes or double quotes as they are implied.

In the example below we define an object that has 2 properties (name and age) along with their associated values.

var person = {     
    name: “Bob”,
    age: 29      // no comma after last property
};

We can add property/value pairs to an object using the dot operator.

person.IQ = 130;

We can also use bracket notation to add new property/value pairs to an object and modify the value associated with an existing property.  The identifier within the brackets can be of any type, however we use usually use strings or integers.  If the identifier within the brackets is not a string, it is automatically converted to a string with toString().

person['grade point average'] = 4.0;    // identifier can include spaces

person['0'] = "CS";  
person[1] = 20;       // 1 is converted to '1'

var a = 'age';
person[a] = 19;

The value of associated with a property can be any valid JavaScript type including other objects and arrays.

var grades = {csci105: 3.2, csci240: 4.0};
person['grades'] = grades;

var colors = ["Blue", "Brown"];
person['favorite colors'] = colors;

A value associated with a property in an object can be access using bracket notation or using the dot operator.  When using bracket notation, the identifier within the brackets must be a string.

console.log("age: " + person.age);
console.log("IQ: " + person['IQ']);

Further reading: Working with Objects

Functions

Named functions are declared using the function keyword followed by the name of the function, a parameter list, and a block of code.  In the example below, the function named sum has two parameters named x and y, and returns the value of x + y.

function sum(x, y) {
    return x + y;
}

Since variables don’t have types when they”re defined, this function attempts to apply the + operator on v1 and v2 regardless of their types. The + operation is overloaded so can work different types.  If either argument is non-numeric, both arguments are cast to strings.

var x = sum(1,2);
console.log(x);                         // prints 3

console.log(sum(3.5, .25));             // prints 3.75
console.log(sum("hello ", "world"));    // prints "hello world"
console.log(sum(true, true));           // prints 2

console.log(sum("hi",2));               // prints "hi2"
console.log(sum(2, {name: "alice"}));   // prints "2[object Object]"
console.log(sum([1,2,3], [2,3,4]));     // prints "1,2,32,3,4"

Named functions are loaded into memory and available at the start of the script.  This is called hoisting and is done during a preprocessing phase.

console.log(sum(1,2));      // called before declared - ok

function sum(x,y) {            // the name sum is hoisted
    return x + y;
}

Unnamed Functions

We can define unnamed functions and assign a variable equal to a reference to the unnamed function.   For example, below we create a variable named errMssg and set its value equal to the reference of an unnamed function that prints to the console an “Error” followed by the value of its argument.

var errMssg = function(x) {
    console.log("Error: " + x);
};                               // must include ; since its an assignment statement

errMssg("Can not compute");      // call using the function reference

Since the variable hold just a reference, not unlike a reference to an object, multiple variables can hold the same reference.

var dup = errMssg;
dup("Out of bounds");

Function references are not hoisted, so we can only call a function using a function reference after the reference has been assigned.

foo();                        // Error: foo is not hoisted

var foo = function () {       // function not hoisted
    console.log(“foo”);
}

Passing Function References

You can pass and return references to functions.

function foo(x) {
    console.log(x);
}

var bar = function(x) {
    console.log(x);
};

function run(fun, str) {
    fun(str);
}

run(foo, “Out of memory”);     // pass named function
run(bar, "Lost signal");       // pass function reference

Another Example

function sum(x,y) {
    return x + y;
}

function run(fun, x, y) {
    return fun(x, y);
}

var x = run(sum, 1, 2);
console.log(x);

Function Internals

Two special properties exist inside a function object: arguments and this.

The arguments property holds an object that contains the values that are passed into the function.

function errMssg(x) {
    console.log(arguments);    // prints [object Arguments] {
}                              //          0: "Testing 1 2 3"
                               //        }

errMssg("Testing 1 2 3");

The arguments object has a property named callee that holds a pointer to the function that owns the arguments object (i.e. the function itself).

function foo() {
    console.log("foo");
    return arguments.callee;
}

var self_ref = foo();
self_ref();

The this Object

Every function is executed on some object.  By default functions are called on a global object named window.    This object is called a context object and has properties like any other object.  The global window object has many properties, including properties for each global variable declared without using the var keyword.

We can access within a function the context object that a function is executed on.  To do so we can use the this property.   For example, below we declare a global variable named color and print its value within sayColor() by

  1. accessing the global variable directly
  2. accessing the global variable through the this object which is the window object
  3. accessing the global variable through the window object
color = "red";

function sayColor() {
    console.log(color);          // prints "red"
    console.log(this.color);     // prints "red"
    console.log(window.color);   // prints "red"
}

sayColor();

We can also call a function on an arbitrary object.  In the example below, this refers to the object whose reference is stored in the variable obj.

function sayColor() {
    console.log(this.color);
}

var obj = {
    color: "blue",
    sayColor: sayColor
};
obj.sayColor();        // prints “blue”

The caller Property

Every function has a caller property that holds a reference to the function that called this function.

var outer = function() {
    inner();
}

function inner() {
    console.log(arguments.callee.caller);
}

inner();     // prints null
outer();     // prints the code of the outer() function

Other Function Properties

Every function has a property named length which holds the number of arguments the function expects.

function foo(x) { }
console.log(foo.length);   // prints 1

Each function also has three methods named apply(), call(), and bind().  The apply() and call() methods allow us to change which context object the function executes on.  This allows us to write a generic function to be used for arbitrary context objects.

window.color = “red”;
var o = {color: “blue”};

function sayColor() {
    console.log(this.color);   // recall this points to the context object
}

sayColor();                    // red - the default context object is the window object
sayColor.call(window);         // red – passing in window explicitly

sayColor.call(o);              // blue

The bind() method takes a context as an argument and creates a new function instance whose context object is set to the argument.  This allows us to create functions that operate on a specific context object.

window.color = “red”;
var o = {color: “blue” };

function sayColor() {
    console.log(this.color);                   
}

sayColor();      // recall the default context object is the window object

var oSayColor = sayColor.bind(o);  // binding o to this inside the function oSayColor
oSayColor();                       // blue