Chapter 4. Objects

Now that you've mastered JavaScript's primitive data types, arrays, and functions, it is time for the best part—objects. In this chapter, you will learn:

  • How to create and use objects

  • What are the constructor functions

  • What types of built-in JavaScript objects exist and what they can do for you

From Arrays to Objects

As you already know from Chapter 2, an array is just a list of values. Each value has an index (a numeric key) starting from zero and incrementing by one for each value.

>>>> var myarr = ['red', 'blue', 'yellow', 'purple'];
>>> myarr;

["red", "blue", "yellow", "purple"]

>>> myarr[0]

"red"

>>> myarr[3]

"purple"

If you put the indexes in one column and the values in another, you'll end up with a table of key/value pairs like this:

Key

Value

0

red

1

blue

2

yellow

3

purple

An object is very similar to an array but with the difference that you define the keys yourself. You're not limited to using only numeric indexes but can use friendlier keys, such as first_name, age, and so on.

Let's take a look at a simple object and examine its parts:

var hero = {
  breed: 'Turtle', 
  occupation: 'Ninja'
};

You can see that:

  • The name of the variable that contains the object is hero

  • Instead of [ and ] which you use to define an array, you use { and } for objects

  • You separate the elements (called properties) contained in the object with commas

  • The key/value pairs are divided by colons, as key: value

The keys (names of the properties) can optionally be placed in quotation marks. For example these are all the same:

var o = {prop: 1};
var o = {"prop": 1};
var o = {'prop': 1};

It's recommended that you don't quote the names of the properties (it is also less typing!), but there are some cases when you have must use quotes:

  • If the property name is one of the reserved words in JavaScript (see Appendix A)

  • If it contains spaces or special characters (anything other than letters, numbers, and the underscore character)

  • If it starts with a number

Basically, if the name you have chosen for a property is not a valid name for a variable in JavaScript, then you need to in place in quotes.

Have a look at this bizarre-looking object:

var o = {
  something: 1, 
  'yes or no': 'yes', 
  '!@#$%^&*': true
};

This is a valid object. The quotes are required for the second and the third properties, otherwise you will get an error.

Later in this chapter you will see other ways to define objects and arrays, in addition to [] and {}. But first, let's introduce this bit of terminology: defining an array with [] is called array literal notation and defining an object using the curly braces {} is called object literal notation.

Elements, Properties, Methods

When talking about arrays, you say that they contain elements. When talking about objects, you say that they contain properties. There isn't any significant difference in JavaScript; it is just the terminology that people are used to, probably from other programming languages.

A property of an object can contain a function, because functions are just data. In this case, you say that this property is a method.

var dog = {
  name: 'Benji', 
  talk: function(){
    alert('Woof, woof!'),
  }
};

It's also possible to store functions as array elements and invoke them, but you will not see much code like this in practice:

>>> var a = [];
>>> a[0] = function(what){alert(what);};
>>> a[0]('Boo!'),

Hashes, Associative Arrays

In some programming languages, there is distinction between:

  • A normal array, also called indexed or enumerated (the keys are numbers) and

  • An associative array, also called a hash (the keys are strings)

JavaScript uses arrays to represent indexed arrays and objects to represent associative arrays. If you want a hash in JavaScript, you use an object.

Accessing Object's Properties

There are two ways to access a property of an object:

  • Using square bracket notation, for example hero['occupation']

  • Using the dot notation, for example hero.occupation

The dot notation is easier to read and write but it cannot always be used. The same rules apply as for quoting property names: if the name of the property is not a valid variable name, you cannot use the dot notation.

Let's take this object:

var hero = {
  breed: 'Turtle',
  occupation: 'Ninja'
};

Accessing a property with the dot notation:

>>> hero.breed;

"Turtle"

Accessing a property with the bracket notation:

>>> hero['occupation'];

"Ninja"

Accessing a non-existing property returns undefined:

>>> 'Hair color is ' + hero.hair_color;

"Hair color is undefined"

Objects can contain any data, including other objects.

var book = {
  name: 'Catch-22',
  published: 1961,
  author: {
    firstname: 'Joseph',
    lastname: 'Heller'
  }
};

To get to the firstname property of the object contained in the author property of the book object, you use:

>>> book.author.firstname

"Joseph"

Or using the square braces notation:

>>> book['author']['lastname']

"Heller"

It works even if you mix both:

>>> book.author['lastname']

"Heller"

>>> book['author'].lastname

"Heller"

One other case where you need square brackets is if the name of the property you need to access is not known beforehand. During runtime, it is dynamically stored in a variable:

>>> var key = 'firstname';
>>> book.author[key];

"Joseph"

Calling an Object's Methods

Because a method is just a property that happens to be a function, you can access methods in the same way as you would access properties: using the dot notation or using square brackets. Calling (invoking) a method is the same as calling any other function: just add parentheses after the method name, which effectively say "Execute!".

var hero = {
  breed: 'Turtle',
  occupation: 'Ninja',
  say: function() {
    return 'I am ' + hero.occupation;
  }
}
>>> hero.say();

"I am Ninja"

If there are any parameters that you want to pass to a method, you proceed as with normal functions:

>>> hero.say('a', 'b', 'c'),

Because you can use the array-like square brackets to access a property, this means you can also use brackets to access and invoke methods, although this is not at a common practice:

>>> hero['say']();

Note

Best Practice Tip: No quotes

1. Use the dot notation to access methods and properties

2. Don't quote properties in your object literals

Altering Properties/Methods

JavaScript is a dynamic language; it allows you to alter properties and methods of existing objects at any time. This includes adding new properties or deleting them. You can start with a blank object and add properties later. Let's see how you can go about doing this.

An empty object:

>>> var hero = {};

Accessing a non-existing property:

>>> typeof hero.breed

"undefined"

Adding some properties and a method:

>>> hero.breed = 'turtle';
>>> hero.name = 'Leonardo';
>>> hero.sayName = function() {return hero.name;};

Calling the method:

>>> hero.sayName();

"Leonardo"

Deleting a property:

>>> delete hero.name;

true

Calling the method again will no longer work:

>>> hero.sayName();

reference to undefined property hero.name

Using this Value

In the previous example, the method sayName() used hero.name to access the name property of the hero object. When you're inside a method though, there is another way to access the object this method belongs to: by using the special value this.

var hero = {
  name: 'Rafaelo',
  sayName: function() {
    return this.name;
  }
}
>>> hero.sayName();

"Rafaelo"

So when you say this, you are actually saying "this object" or "the current object".

Constructor Functions

There is another way to create objects: by using constructor functions. Let's see an example:

function Hero() {
  this.occupation = 'Ninja';
}

In order to create an object using this function, you use the new operator, like this:

>>> var hero = new Hero();
>>> hero.occupation;

"Ninja"

The benefit of using constructor functions is that they accept parameters, which can be used when creating new objects. Let's modify the constructor to accept one parameter and assign it to the name property.

function Hero(name) {
  this.name = name;
  this.occupation = 'Ninja';
  this.whoAreYou = function() {
    return "I'm " + this.name + " and I'm a " + this.occupation;
  }
}

Now you can create different objects using the same constructor:

>>> var h1 = new Hero('Michelangelo'),
>>> var h2 = new Hero('Donatello'),
>>> h1.whoAreYou();

"I'm Michelangelo and I'm a Ninja"

>>> h2.whoAreYou();

"I'm Donatello and I'm a Ninja"

By convention, you should capitalize the first letter of your constructor functions so that you have a visual clue that this is not a normal function. If you call a function that is designed to be a constructor, but you omit the new operator, this is not an error, but it may not behave as you could expect.

>>> var h = Hero('Leonardo'),
>>> typeof h

"undefined"

What happened here? As there was no new operator, we didn't create a new object. The function was called like any other function, so h contains the value that the function returns. The function does not return anything (there's no return), so it actually returns undefined, which gets assigned to h.

In this case, what does this refer to? It refers to the global object.

The Global Object

Previously we discussed global variables (and how you should avoid them) and also the fact that JavaScript programs run inside a host environment (the browser for example). Now that you know about objects, it is time for the whole truth: the host environment provides a global object and all global variables are actually properties of the global object.

If your host environment is the web browser, the global object is called window.

As an illustration, you can try declaring a global variable, outside of any function, such as:

>>> var a = 1;

Then you can access this global variable in various ways:

  • As a variable a

  • As a property of the global object, for example window['a'] or window.a

Let's go back to the case where you define a constructor function and call it without the new operator. In such cases this refers to the global object and all properties set with this become properties of window.

Declaring a constructor function and calling it without new, returns "undefined":

>>> function Hero(name) {this.name = name;}
>>> var h = Hero('Leonardo'),
>>> typeof h

"undefined"

>>> typeof h.name

h has no properties

Because you had this inside Hero, a global variable (a property of the global object) called name was created.

>>> name

"Leonardo"

>>> window.name

"Leonardo"

If you call the same constructor function but this time using new, then a new object is returned and this refers to it.

>>> var h2 = new Hero('Michelangelo'),
>>> typeof h2

"object"

>>> h2.name

"Michelangelo"

The global functions you saw in Chapter 3 can also be invoked as methods of the window object. So the following two codes are equivalent:

>>> parseInt('101 dalmatians')

101

>>> window.parseInt('101 dalmatians')

101

constructor Property

When an object is created, a special property is assigned to it behind the scenes—the constructor property. It contains a reference to the constructor function used to create this object.

Continuing from the previous example:

>>> h2.constructor

Hero(name)

Because the constructor property contains a reference to a function, you might as well call this function to produce a new object. The following code is like saying, "I don't care how object h2 was created, but I want another one just like it".

>>> var h3 = new h2.constructor('Rafaello'),
>>> h3.name;

"Rafaello"

If an object was created using the object literal notation, its constructor is the built-in Object() constructor function (more about this later in this chapter).

>>> var o = {};
>>> o.constructor;

Object()

>>> typeof o.constructor;

"function"

instanceof Operator

Using the instanceof operator, you can test if an object was created with a specific constructor function:

>>> function Hero(){}
>>> var h = new Hero();
>>> var o = {};
>>> h instanceof Hero;

true

>>> h instanceof Object;

false

>>> o instanceof Object;

true

Note that you don't put parentheses after the function name (don't use h instanceof Hero()). This is because you're not invoking this function, but just referring to it by name, as for any other variable.

Functions that Return Objects

In addition to using constructor functions and the new operator to create objects, you can also use a normal function and create objects without new. You can have a function that does some preparatory work and has an object as a return value.

For example, here's a simple factory() function that produces objects:

function factory(name) {
  return {
    name: name
  };
}

Using the factory():

>>> var o = factory('one'),
>>> o.name

"one"

>>> o.constructor

Object()

In fact, you can also use constructor functions and return objects, different from this. This means you can modify the default behavior of the constructor function. Let's see how.

Here's the normal constructor scenario:

>>> function C() {this.a = 1;}
>>> var c = new C();
>>> c.a

1

But now look at this scenario:

>>> function C2() {this.a = 1; return {b: 2};}
>>> var c2 = new C2();
>>> typeof c2.a

"undefined"

>>> c2.b

2

What happened here? Instead of returning the object this, which contains the property a, the constructor returned another object that contains the property b. This is possible only if the return value is an object. Otherwise, if you try to return anything that is not an object, the constructor will proceed with its usual behavior and return this.

Passing Objects

When you copy an object or pass it to a function, you only pass a reference to that object. Consequently, if you make a change to the reference, you are actually modifying the original object.

Here's an example of how you can assign an object to another variable and then make a change to the copy. As a result, the original object is also changed:

>>> var original = {howmany: 1};
>>> var copy = original;
>>> copy.howmany

1

>>> copy.howmany = 100;

100

>>> original.howmany

100

The same thing applies when passing objects to functions:

>>> var original = {howmany: 100};
>>> var nullify = function(o) {o.howmany = 0;}
>>> nullify(original);
>>> original.howmany

0

Comparing Objects

When you compare objects, you'll get true only if you compare two references to the same object. Comparing two distinct objects that happen to have the exact same methods and properties will return false.

Let's create two objects that look the same:

>>> var fido  = {breed: 'dog'};
>>> var benji = {breed: 'dog'};

Comparing them will return false:

>>> benji === fido

false

>>> benji == fido

false

You can create a new variable mydog and assign one of the objects to it, this way mydog actually points to the same object.

>>> var mydog = benji;

In this case benji is mydog because they are the same object (changing mydog's properties will change benji's). The comparison returns true.

>>> mydog === benji

true

And because fido is a different object, it does not compare to mydog:

>>> mydog === fido

false

Objects in the Firebug Console

Before diving into the built-in objects in JavaScript, let's quickly say a few words about working with objects in the Firebug console.

After playing around with the examples in this chapter, you might have already noticed how objects are displayed in the console. If you create an object and type its name, you'll get a string representation of the object including the properties (but only the first few properties if there are too many of them).

Objects in the Firebug Console

The object is clickable and takes you to the DOM tab in Firebug, which lists all of the properties of the object. If a property is also an object, there is a plus ( + ) sign to expand it. This is handy as it gives you an insight into exactly what this object contains.

Objects in the Firebug Console

The console also offers you an object called console and some methods, such as console.log(), console.error(), and console.info() which you can use to display any value you want in the console.

Objects in the Firebug Console

console.log() is convenient when you want to quickly test something, as well as in your real scripts when you want to dump some intermediate debuging information. Here's how you can experiment with loops, for example:

>>> for(var i = 0; i < 5; i++) { console.log(i); }

0

1

2

3

4

Built-in Objects

Earlier in this chapter we came across the Object() constructor function. It is returned when you create objects with the object literal notation and access their constructor property. Object() is one of the built-in constructors; there are others and in the rest of this chapter you'll see all of them.

The built-in objects can be divided into three groups:

  • Data wrapper objects—Object, Array, Function, Boolean, Number, and String. These objects correspond to the different data types in JavaScript. Basically, there is a data wrapper object for each different value returned by typeof (discussed in Chapter 2) with the exception of "undefined" and "null".

  • Utility objects—These are Math, Date, RegExp and can come in very handy.

  • Error objects—The generic Error object as well as other, more specific objects that can help your program recover its working state when something unexpected happens.

Only a handful of methods of the built-in objects will be discussed in this chapter. For a full reference, see Appendix C.

If you're confused about what is a built-in object and what is a built-in constructor, well, they are the same thing. In a moment, you will see how functions, and therefore constructor functions, are also objects.

Object

Obj ect is the parent of all JavaScript objects, which means that every object you create inherits from it. To create a new empty object you can use the literal notation or the Object() constructor function. The following two lines are equivalent:

>>> var o = {};
>>> var o = new Object();

An empty object is not completely useless because it already contains some methods and properties. Let's see a few:

  • o.constructor property returns the constructor function

  • o.toString() is a method that returns a string representation of the object

  • o.valueOf() returns a single-value representation of the object, often this is the object itself

Let's see these methods in action. First, create an object:

>>> var o = new Object();

Calling toString() returns a string representation of the object.

>>> o.toString()

"[object Object]"

toString() will be called internally by JavaScript, when an object is used in a string context. For example alert() works only with strings, so if you call the alert() function passing an object, the method toString() will be called behind the scenes. These two lines will produce the same result:

>>> alert(o)
>>> alert(o.toString())

Ano ther type of string context is the string concatenation. If you try to concatenate an object with a string, the object's toString() will be called first:

>>> "An object: " + o

"An object: [object Object]"

valueOf() is another method that all objects provide. For the simple objects (whose constructor is Object()) the valueOf() method will return the object itself.

>>> o.valueOf() === o

true

To summarize:

  • You can create objects either with var o = {}; (object literal notation, the preferred method) or with var o = new Object();

  • Any object, no matter how complex, inherits from the Object object and therefore offers methods such as toString() and properties such as constructor.

Array

Array() is a built-in function that you can use as a constructor to create arrays:

>>
> var a = new Array();

This is equivalent to the array literal notation:

>>> var a = [];

No matter how the array is created, you can add elements to it as usual:

>>> a[0] = 1; a[1] = 2; a;

[1, 2]

When using the Array() constructor, you can also pass values which will be assigned to the new array's elements.

>>> var a = new Array(1,2,3,'four'),
>>> a;

[1, 2, 3, "four"]

An exception to this is when you pass a single number to the constructor. In this case, the number passed will be considered to be the length of the array.

>>> var a2 = new Array(5);
>>> a2;

[undefined, undefined, undefined, undefined, undefined]

Because arrays are created with a constructor, does this mean that arrays are in fact objects? Yes, and you can verify this by using the typeof operator:

>>> typeof a;

"object"

Because arrays are objects, this means that they inherit the properties and methods of the parent Object.

>>> a.toString();

"1,2,3,four"

>>> a.valueOf()

[1, 2, 3, "four"]

>>> a.constructor

Array()

Arrays are objects, but of a special type because:

  • The names of their properties are automatically assigned using numbers starting from 0

  • They have a length property which contains the number of elements in the array

  • They have additional built-in methods in addition to those inherited from the parent object

Let's examine the differences between an array and an object, starting by creating the empty object o and the empty array a:

>>> var a = [], o = {};

Array objects have a length property automatically defined for them, while normal objects do not:

>>> a.length

0

>>> typeof o.length

"undefined"

It's OK to add both numeric and non-numeric properties to both arrays and objects:

>>> a[0] = 1; o[0] = 1;
>>> a.prop = 2; o.prop = 2;

The length property is always up-to-date with the number of numeric properties, ignoring the non-numeric ones.

>>> a.length

1

The length property can also be set by you. Setting it to a greater value than the current number of items in the array creates empty elements (with a value of undefined).

>>> a.length = 5

5

>>> a

[1, undefined, undefined, undefined, undefined]

Setting the length to a lower value removes the trailing elements:

>>> a.length = 2;

2

>>> a

[1, undefined]

Interesting Array Methods

In addition to the methods inherited from the parent object, array objects also have some more useful methods, such as sort(), join(), and slice(), among others (see Appendix C for the full list).

Let's take one array and experiment with some of these methods:

>>> var a = [3, 5, 1, 7, 'test'];

The push() method appends a new element at the end of the array. The pop() method removes the last element. a.push('new') works just like a[a.length] = 'new' and a.pop() is the same as a.length--.

push() returns the length of the changed array, pop() returns the element that it removed.

>>> a.push('new')

6

>>> a

[3, 5, 1, 7, "test", "new"]

>>> a.pop()

"new"

>>> a

[3, 5, 1, 7, "test"]

The sort() method sorts the array and returns the modified array. In the next example, after the sort, both a and b contain pointers to the same array.

>>> var b = a.sort();
>>> b

[1, 3, 5, 7, "test"]

>>> a

[1, 3, 5, 7, "test"]

join() returns a string containing the values of all the elements in the array, concatenated together using the string parameter passed to join()

>>> a.join(' is not '),

"1 is not 3 is not 5 is not 7 is not test"

slice() returns a piece of the array without modifying the source array. The first parameter to slice() is the start index and the second is the end index (both indices are zero-based).

>>> b = a.slice(1, 3);

[3, 5]

>>> b = a.slice(0, 1);

[1]

>>> b = a.slice(0, 2);

[1, 3]

After all the slicing, the source array is still the same:

>>> a

[1, 3, 5, 7, "test"]

splice() modifies the source array. It removes a slice, returns it, and optionally fills the gap with new elements. The first two parameters define start and end of the slice to be removed; the other parameters pass the new values.

>>> b = a.splice(1, 2, 100, 101, 102);

[3, 5]

>>> a

[1, 100, 101, 102, 7, "test"]

Filling the gap with new elements is optional and you can skip it:

>>> a.splice(1, 3)

[100, 101, 102]

>>> a

[1, 7, "test"]

Function

You already know that functions are a special data type. But it turns out that there's more to it than that—functions are actually objects. There is a built-in constructor function called Function() which allows an alternative (but not recommended) way to create a function.

The following three ways of defining a function are equivalent:

>>> function sum(a, b) {return a + b;};
>>> sum(1, 2)

3

>>> var sum = function(a, b) {return a + b;};
>>> sum(1, 2)

3

>>> var sum = new Function('a', 'b', 'return a + b;'),
>>> sum(1, 2)

3

When using the Function() constructor, you pass the parameter names first (as strings) and then the source code for the body of the function (again as a string). The JavaScript engine then needs to evaluate the source code you pass and create the new function for you. This source code evaluation suffers from the same drawbacks as the eval() function, so defining functions using the Function() constructor should be avoided when possible.

If you use the Function constructor to create functions that have lots of parameters, bear in mind that the parameters can be passed as a single comma-delimited list, so, for example, these are the same:

>>> var first = new Function('a, b, c, d', 'return arguments;'),
>>> first(1,2,3,4);

[1, 2, 3, 4]

>>> var second = new Function('a, b, c', 'd', 'return arguments;'),
>>> second(1,2,3,4);

[1, 2, 3, 4]

>>> var third = new Function('a', 'b', 'c', 'd', 
                             'return arguments;'),
>>> third(1,2,3,4);

[1, 2, 3, 4]

Note

Best Practice

Do not use the Function() constructor. As with eval() and setTimeout() (discussed further in the book), always try to stay away from cases where you pass JavaScript code as a string.

Properties of the Function Objects

Like any other object, functions have a constructor property that contains a reference to the Function() constructor function.

>>> function myfunc(a){return a;}
>>> myfunc.constructor

Function()

Functions also have a length property, which contains the number of parameters the function accepts.

>>> function myfunc(a, b, c){return true;}
>>> myfunc.length

3

There is another interesting property, which doesn't exist in the ECMA standard, but is implemented across the browsers—the caller property. This returns a reference to the function that called our function. Let's say there is a function A() that gets called from function B(). If inside A() you put A.caller, it will return the function B().

>>> function A(){return A.caller;}
>>> function B(){return A();}
>>> B()

B()

This could be useful if you want your function to respond differently depending on the function from which it was called. If you call A() from the global space (outside of any function), A.caller will be null.

>>> A()

null

The most important property of a function is the prototype property. We'll discuss this property in detail in the next chapter, but for now let's just say this:

  • The prototype property of a function contains an object

  • It is only useful when you use this function as a constructor

  • All objects created with this function keep a reference to the prototype property and can use its properties as their own

Let's see a quick example to demonstrate the prototype property. Let's start with a simple object that has a property name and a method say().

var some_obj = {
  name: 'Ninja',
  say: function(){
    return 'I am a ' + this.name;
  }
}

If you create a hollow function, you can verify that it automatically has a prototype property that contains an empty object.

>>> function F(){} 
>>> typeof F.prototype 

"object"

It gets interesting when you modify the prototype property. You can replace the default empty object with any other object. Let's assign our some_obj to the prototype.

>>> F.prototype = some_obj;

Now, using the function F() as a constructor function, you can create a new object obj which will have access to the properties of F.prototype as if it were its own.

>>> var obj = new F();
>>> obj.name 

"Ninja"

>>> obj.say()

"I am a Ninja"

There will be more about the prototype property in the next chapter.

Methods of the Function Objects

The function objects, being a descendant of the top parent Object, get the default methods, such as toString(). When invoked on a function, the toString() method returns the source code of the function.

>>> function myfunc(a, b, c) {return a + b + c;}
>>> myfunc.toString()

"function myfunc(a, b, c) {

return a + b + c;

}"

If you try to peek into the source code of the built-in functions, you'll get the hardly useful [native code] string:

>>> eval.toString()

"function eval() {

[native code]

}"

Two useful methods of the function objects are call() and apply(). They allow your objects to borrow methods from other objects and invoke them as their own. This is an easy and powerful way to reuse code.

Let's say you have a some_obj object, which contains the method say()

var some_obj = {
  name: 'Ninja', 
  say: function(who){
    return 'Haya ' + who + ', I am a ' + this.name; 
  }
}

You can call the say() method which internally uses this.name to gain access to its own name property.

>>> some_obj.say('Dude'),

"Haya Dude, I am a Ninja"

Now let's create a simple object my_obj, which only has a name property:

>>> my_obj = {name: 'Scripting guru'};

my_obj likes some_obj's say() method so much that it wants to invoke it as its own. This is possible using the call() method of the say() function object:

>>> some_obj.say.call(my_obj, 'Dude'),

"Haya Dude, I am a Scripting guru"

It worked! But what happened here? We invoked the call() method of the say() function object passing two parameters: the object my_obj and the string 'Dude'. The result is that when say() was invoked, the references to this value that it contains, pointed to my_obj. This way this.name didn't return Ninja, but Scripting guru instead.

If you have more parameters to pass when invoking the call() method, you just keep adding them:

some_obj.someMethod.call(my_obj, 'a', 'b', 'c'),

If you don't pass an object as a first parameter to call() or pass null, the global object will be assumed.

The method apply() works the same way as call() but with the difference that all parameters you want to pass to the method of the other object are passed as an array. The following two lines are equivalent:

some_obj.someMethod.apply(my_obj, ['a', 'b', 'c']);
some_obj.someMethod.call(my_obj, 'a', 'b', 'c'),

Continuing the example above, you can use:

>>> some_obj.say.apply(my_obj, ['Dude']);

"Haya Dude, I am a Scripting guru"

The arguments Object Revisited

In the previous chapter, you saw how, from inside a function, you have access to something called arguments, which contains the values of all parameters passed to the function:

>>> function f() {return arguments;}
>>> f(1,2,3)

[1, 2, 3]

arguments looks like an array but is actually an array-like object. It resembles an array because it contains indexed elements and a length property. However, the similarity ends here, as arguments doesn't provide any of the array methods, such as sort() or slice().

The arguments object has another interesting property—the callee property. This contains a reference to the function being called. If you create a function that returns arguments.callee and you call this function, it will simply return a reference to itself.

>>> function f(){return arguments.callee;}
>>> f()

f()

arguments.callee allows anonymous functions to call themselves recursively. Here's an example:

(
  function(count){
    if (count < 5) {
      alert(count); 
      arguments.callee(++count);
    }
  }
)(1)

Here you have an anonymous function that receives a count parameter, alerts it, and then calls itself with an incremented count. The whole function is wrapped in parentheses and followed by another set of parentheses, which invokes the function right away, passing the initial value 1. The result of this code is four alerts showing the numbers 1, 2, 3, and 4.

Boolean

Our journey through the built-in objects in JavaScript continues, and the next ones are fairly easy; they merely wrap the primitive data types boolean, number, and string.

You already know a lot about booleans from Chapter 2. Now, let's meet the Boolean() constructor:

>>> var b = new Boolean();

It is important to note that this creates a new object b, and not a primitive boolean value. To get the primitive value, you can call the valueOf() method (inherited from Object).

>>> var b = new Boolean();
>>> typeof b

"object"

>>> typeof b.valueOf()

"boolean"

>>> b.valueOf()

false

Overall, objects created with the Boolean() constructor are not too useful, as they don't provide any methods or properties, other than the inherited ones.

The Boolean() function is useful when called as a normal function, without new. This converts non-booleans to booleans (which is the same as using a double negation !!value).

>>> Boolean("test")

true

>>> Boolean("")

false

>>> Boolean({})

true

Apart from the six falsy values, everything else is truthy in JavaScript, including the empty objects. This also means that all boolean objects created with new Boolean() evaluate to true, as they are objects.

Let's create two boolean objects, one truthy and one falsy:

>>> var b1 = new Boolean(true)
>>> b1.valueOf()

true

>>> var b2 = new Boolean(false)
>>> b2.valueOf()

false

Now let's convert them to primitive boolean values. They both convert to true because all objects are truthy.

>>> Boolean(b1)

true

>>> Boolean(b2)

true

Number

Similarly to Boolean(), the Number() function can be used:

  • As a normal function in order to try to convert any value to a number. This is similar to the use of parseInt() or parseFloat().

  • As a constructor function (with new) to create objects

>>> var n = Number('12.12'),
>>> n

12.12

>>> typeof n

"number"

>>> var n = new Number('12.12'),
>>> typeof n

"object"

Because functions are objects, they can have properties. The Number() function contains some interesting built-in properties (which you cannot modify):

>>> Number.MAX_VALUE

1.7976931348623157e+308

>>> Number.MIN_VALUE

5e-324

>>> Number.POSITIVE_INFINITY

Infinity

>>> Number.NEGATIVE_INFINITY

-Infinity

>>> Number.NaN

NaN

The number objects provide three methods—toFixed(), toPrecision() and toExponential() (see Appendix C for details).

>>> var n = new Number(123.456)
>>> n.toFixed(1)

"123.5"

Note that you can use these methods without explicitly creating a number object. In such cases, the number object will be created (and destroyed) for you behind the scenes:

>>> (12345).toExponential()

"1.2345e+4"

As with all objects, number objects also provide the toString() method. It is interesting to note that this method accept an optional radix parameter (10 is the default).

>>> var n = new Number(255);
>>> n.toString();

"255"

>>> n.toString(10);

"255"

>>> n.toString(16);

"ff"

>>> (3).toString(2);

"11"

>>> (3).toString(10);

"3"

String

Using the String() constructor function you can create string objects. Objects produced this way provide some useful methods when it comes to text manipulation, but if you don't plan on using these methods, you're probably better off just using primitive strings.

Here's an example that shows the difference between a string object and a primitive string data type.

>>> var primitive = 'Hello';
>>> typeof primitive;

"string"

>>> var obj = new String('world'),
>>> typeof obj;

"object"

A string object is very similar to an array of characters. The string objects have an indexed property for each character and they also have a length property.

>>> obj[0]

"w"

>>> obj[4]

"d"

>>> obj.length

5

To extract the primitive value from the string object, you can use the valueOf() or toString() methods inherited from Object. You'll probably never need to do this, as toString() is called behind the scenes if you use an object in a string context.

>>> obj.valueOf()

"world"

>>> obj.toString()

"world"

>>> obj + ""

"world"

Primitive strings are not objects, so they don't have any methods or properties. But JavaScript still offers you the syntax to treat primitive strings as objects.

In the following example, string objects are being created (and then destroyed) behind the scenes every time you access a primitive string as if it was an object:

>>> "potato".length

6

>>> "tomato"[0]

"t"

>>> "potato"["potato".length - 1]

"o"

One final example to illustrate the difference between a string primitive and a string object: let's convert them to boolean. The empty string is a falsy value, but any string object is truthy.

>>> Boolean("")

false

>>> Boolean(new String(""))

true

Similarly to Number() and Boolean(), if you use the String() function without new, it converts the parameter to a primitive string. This means calling toString() method, if the input is an object.

>>> String(1)

"1"

>>> String({p: 1})

"[object Object]"

>>> String([1,2,3])

"1,2,3"

Interesting Methods of the String Objects

Let's experiment with some of the methods you can call for string objects (see Appendix C for the full list).

Start off by creating a string object:

>>> var s = new String("Couch potato");

toUpperCase() and toLowerCase() are convenient ways to transform the capitalization of the string:

>>> s.toUpperCase()

"COUCH POTATO"

>>> s.toLowerCase()

"couch potato"

charAt() tells you the character found at the position you specify, which is the same as using square brackets (a string is an array of characters).

>>> s.charAt(0);

"C"

>>> s[0]

"C"

If you pass a non-existing position to charAt(), you get an empty string:

>>> s.charAt(101)

""

indexOf() allows you to search within a string. If there is a match, the method returns the position at which the first match is found. The position count starts at 0, so the second character in "Couch" is "o" at position 1.

>>> s.indexOf('o')

1

You can optionally specify where (at what position) to start the search. The following finds the second "o", because indexOf() is instructed to start the search at position 2:

>>> s.indexOf('o', 2)

7

lastIndexOf() starts the search from the end of the string (but the position of the match is still counted from the beginning):

>>> s.lastIndexOf('o')

11

You can search for strings, not only characters, and the search is case sensitive:

>>> s.indexOf('Couch')

0

If there is no match, the function returns a position -1:

>>> s.indexOf('couch')

-1

To perform a case-insensitive search you can transform the string to lowercase first and then search it:

>>> s.toLowerCase().indexOf('couch')

0

When you get 0, this means that the matching part of the string starts at position 0. This can cause confusion when you check with if, because if will convert the position 0 to a boolean false. So while this is syntactically correct, it is logically wrong:

if (s.indexOf('Couch')) {...}

The proper way to check if a string contains another string is to compare the result of indexOf() to the number -1.

if (s.indexOf('Couch') !== -1) {...}

slice() and substring() return a piece of the string when you specify start and end positions:

>>> s.slice(1, 5)

"ouch"

>>> s.substring(1, 5)

"ouch"

Note that the second parameter you pass is the end position, not the length of the piece. The difference between these two methods is how they treat negative arguments. substring() treats them as zeros, while slice() adds them to the length of the string. So if you pass parameters (1, -1) it's the same as substring(1, 0) and slice(1, s.length - 1):

>>> s.slice(1, -1)

"ouch potat"

>>> s.substring(1, -1)

"C"

The split() method creates an array from the string, using a string that you pass as a separator:

>>> s.split(" ")

["Couch", "potato"]

split() is the opposite of join() which creates a string from an array:

>>> s.split(' ').join(' '),

"Couch potato"

concat() glues strings together, the way the + operator does for primitive strings:

>>> s.concat("es")

"Couch potatoes"

Note that while some of the methods discussed above return new primitive strings, none of them modify the source string. After all the methods we called, our initial string is still the same:

>>> s.valueOf()

"Couch potato"

We looked at indexOf() and lastIndexOf() to search within strings, but there are more powerful methods (search(), match(), and replace()) which take regular expressions as parameters. You'll see these later, when we get to the RegExp() constructor function.

At this point we're done with all of the data wrapper objects, so off we go to the utility objects Math, Date, and RegExp.

Math

Math is a little different from the other built-in global objects you saw above. It's not a normal function and therefore cannot be used with new to create objects. Math is a built-in global object, which provides a number of methods and properties that are useful for mathematical operations.

Math's properties are constants so you can't change their values. Their names are all in upper case to emphasize the difference between them and a normal variable property. Let's see some of these constant properties:

The number π:

>>> Math.PI

3.141592653589793

Square root of 2:

>>> Math.SQRT2

1.4142135623730951

Euler's constant e:

>>> Math.E

2.718281828459045

Natural logarithm of 2:

>>> Math.LN2

0.6931471805599453

Natural logarithm of 10:

>>> Math.LN10

2.302585092994046

Now you know how to impress your friends the next time they (for whatever awkward reason) start wondering, "What was the value of e? I can't remember." Just type Math.E in the console and you have the answer.

Let's take a look at some of the methods the Math object provides (the full list is in Appendix C).

Generating random numbers:

>>> Math.random() 

0.3649461670235814

random() returns a number between 0 and 1, so if you want a number between, let's say 0 and 100, you can do:

>>> 100 * Math.random()

For numbers between any two values min and max, use the formula ((max - min) * Math.random()) + min. For example, a random number between 2 and 10 would be:

>>> 8 * Math.random() + 2

9.175650496668485

If you only need an integer, you can use one of rounding methods—floor() to round down, ceil() to round up or round() to round to the nearest. For example to get either 0 or 1:

>>> Math.round(Math.random())

If you need the lowest or the highest among a set of numbers, you have the methods min() and max(). So if you have a form on a page that asks for a valid month, you can make sure that you always work with sane data:

>>> Math.min(Math.max(1, input), 12)

The Math object also provides you with the ability to perform mathematical operations for which you don't have a designated operator. This means that you can raise to a power using pow(), find the square root using sqrt() and perform all the trigonometric operations—sin(), cos(), atan(), and so on.

2 to the power of 8:

>>> Math.pow(2, 8)

256

Square root of 9:

>>> Math.sqrt(9)

3

Date

Date() is a constructor function that creates date objects. You can create a new object by passing:

  • Nothing (defaults to today's date)

  • A date-like string

  • Separate values for day, month, time, and so on

  • A timestamp

An object instantiated with today's date/time:

>>> new Date()

Tue Jan 08 2008 01:10:42 GMT-0800 (Pacific Standard Time)

(As with all objects, the Firefox console displays the result of the toString() method, so this long string "Tue Jan 08...." is what you get when you call toString() on a date object.)

Here are some examples of using strings to initialize a date object. It's interesting how many different formats you can use to specify the date.

>>> new Date('2009 11 12')

Thu Nov 12 2009 00:00:00 GMT-0800 (Pacific Standard Time)

>>> new Date('1 1 2012')

Sun Jan 01 2012 00:00:00 GMT-0800 (Pacific Standard Time)

>>> new Date('1 mar 2012 5:30')

Thu Mar 01 2012 05:30:00 GMT-0800 (Pacific Standard Time)

It is good that JavaScript can figure out a date from different strings, but this is not really a reliable way of defining a precise date. The better way is to pass numeric values to the Date() constructor representing:

  • Year

  • Month: 0 (January) to 11 (December)

  • Day: 1 to 31

  • Hour: 0 to 23

  • Minutes: 0 to 59

  • Seconds: 0 to 59

  • Milliseconds: 0 to 999

Let's see some examples.

Passing all the parameters:

>>> new Date(2008, 0, 1, 17, 05, 03, 120)

Tue Jan 01 2008 17:05:03 GMT-0800 (Pacific Standard Time)

Passing date and hour:

>>> new Date(2008, 0, 1, 17)

Tue Jan 01 2008 17:00:00 GMT-0800 (Pacific Standard Time)

Watch out for the fact that the month starts from 0, so 1 is February:

>>> new Date(2008, 1, 28)

Thu Feb 28 2008 00:00:00 GMT-0800 (Pacific Standard Time)

If you pass a value greater than allowed, your date "overflows" forward. Because there's no February 30 in 2008, this means it has to be March 1st (remember that 2008 was a leap-year).

>>> new Date(2008, 1, 29)

Fri Feb 29 2008 00:00:00 GMT-0800 (Pacific Standard Time)

>>> new Date(2008, 1, 30)

Sat Mar 01 2008 00:00:00 GMT-0800 (Pacific Standard Time)

Similarly, Dec 32nd becomes Jan 01st of the next year:

>>> new Date(2008, 11, 31)

Wed Dec 31 2008 00:00:00 GMT-0800 (Pacific Standard Time)

>>> new Date(2008, 11, 32)

Thu Jan 01 2009 00:00:00 GMT-0800 (Pacific Standard Time)

Finally, a date object can be initialized with a timestamp (the number of milliseconds since the UNIX epoch, where 0 milliseconds is 1st January 1970).

>>> new Date(1199865795109)

Wed Jan 09 2008 00:03:15 GMT-0800 (Pacific Standard Time)

If you call Date() without new, you get a string representing the current date, whether or not you pass any parameters. This gives the current time (current when this example was run):

>>> Date()

"Thu Jan 17 2008 23:11:32 GMT-0800 (Pacific Standard Time)"

>>> Date(1, 2, 3, "it doesn't matter");

"Thu Jan 17 2008 23:11:35 GMT-0800 (Pacific Standard Time)"

Methods to Work with Date Objects

Once you've created a date object, there are lots of methods you can call on that object. Most of the methods can be divided into set*() and get*() methods. For example getMonth(), setMonth(), getHours(), setHours(), and so on. Let's see some examples.

Creating a date object:

>>> var d = new Date();
>>> d.toString();

"Wed Jan 09 2008 00:26:39 GMT-0800 (Pacific Standard Time)"

Setting the month to March (months start from 0):

>>> d.setMonth(2);

1205051199562

>>> d.toString();

"Sun Mar 09 2008 00:26:39 GMT-0800 (Pacific Standard Time)"

Getting the month:

>>> d.getMonth();

2

In addition to all the methods of the date instances, there are also two methods that are properties of the Date() function/object. These do not need a date instance; they work just like Math's methods. In class-based languages, such methods would be called "static" because they don't require an instance.

Date.parse() takes a string and returns a timestamp:

>>> Date.parse('Jan 1, 2008')

1199174400000

Date.UTC() takes all parameters for year, month, day, and so on, and produces a timestamp in Universal time.

>>> Date.UTC(2008, 0, 1)

1199145600000

Because the new Date() constructor can accept timestamps, you can pass the result of Date.UTC() to it. Using the following example you can see how UTC() works with universal time, while new Date() works with local time:

>>> new Date(Date.UTC(2008, 0, 1));

Mon Dec 31 2007 16:00:00 GMT-0800 (Pacific Standard Time)

>>> new Date(2008, 0, 1);

Tue Jan 01 2008 00:00:00 GMT-0800 (Pacific Standard Time)

Let's see one final example of working with the Date object. I was curious about which day my birthday falls on in 2012:

>>> var d = new Date(2012, 5, 20);
>>> d.getDay();

3

Starting the count from 0 (Sunday), 3 means Wednesday. Is that so?

>>> d.toDateString();

"Wed Jun 20 2012"

OK, Wednesday is good but not necessarily the best day for a party. So how about a loop that tells how many times June 20 is a Friday from year 2012 to year 3012. Actually, let's see the distribution of all the days of the week. (After all, with all the medical progress, we're all going to be alive and kicking in 3012.)

First, let's initialize an array with seven elements, one for each day of the week. These will be used as counters. As we loop our way up to 3012, we'll increment the counters.

var stats = [0,0,0,0,0,0,0];

The loop:

for (var i = 2012; i < 3012; i++) {
  stats[new Date(i, 5, 20).getDay()]++;
}

And the result:

>>> stats;

[139, 145, 139, 146, 143, 143, 145]

143 Fridays and 145 Saturdays. Woo-hoo!

RegExp

Regular expressions provide a powerful way to search and manipulate text. If you're familiar with SQL, you can think of regular expressions as being somewhat similar to SQL: you use SQL to find and update data inside a database, and you use regular expressions to find and update data inside a piece of text.

Different languages have different implementations (think "dialects") of the regular expressions syntax. JavaScript uses the Perl 5 syntax.

Instead of saying "regular expression", people often shorten it to "regex" or "regexp".

A regular expression consists of:

  • A pattern you use to match text

  • Zero or more modifiers (also called flags) that provide more instructions on how the pattern should be applied

The pattern can be as simple as literal text to be matched verbatim, but that is rare and in such cases you're better off using indexOf(). Most of the times, the pattern is more complex and could be difficult to understand. Mastering regular expressions patterns is a large topic, which won't be discussed in details here; instead, you'll see what JavaScript provides in terms of syntax, objects and methods in order to support the use of regular expressions. You can also refer to Appendix D as a reference when writing patterns.

JavaScript provides the RegExp() constructor which allows you to create regular expression objects.

>>> var re = new RegExp("j.*t"); 

There is also the more convenient regexp literal:

>>> var re = /j.*t/; 

In the example above, j.*t is the regular expression pattern. It means, "Match any string that starts with j, ends with t and has zero or more characters in between". The asterisk * means "zero or more of the preceding"; the dot (.) means "any character". The pattern needs to be placed in quotation marks when used in a RegExp() constructor.

Properties of the RegExp Objects

The regular expression objects have the following properties:

  • global: If this property is false, which is the default, the search stops when the first match is found. Set this to true if you want all matches.

  • ignoreCase: Case sensitive match or not, defaults to false.

  • multiline: Search matches that may span over more than one line, defaults to false.

  • lastIndex: The position at which to start the search, defaults to 0.

  • source: Contains the regexp pattern.

None of these properties, except for lastIndex, can be changed once the object has created.

The first three parameters represent the regex modifiers. If you create a regex object using the constructor, you can pass any combination of the following characters as a second parameter:

  • "g" for global

  • "i" for ignoreCase

  • "m" for multiline

These letters can be in any order. If a letter is passed, the corresponding modifier is set to true. In the following example, all modifiers are set to true:

>>> var re = new RegExp('j.*t', 'gmi'), 

Let's verify:

>>> re.global;

true

Once set, the modifier cannot be changed:

>>> re.global = false;
>>> re.global

true

To set any modifiers using the regex literal, you add them after the closing slash.

>>> var re = /j.*t/ig;
>>> re.global

true

Methods of the RegExp Objects

The regex objects provide two methods you can use to find matches: test() and exec(). They both accept a string parameter. test() returns a boolean (true when there's a match, false otherwise), while exec() returns an array of matched strings. Obviously exec() is doing more work, so use test() unless you really need to do something with the matches. People often use regular expressions for validation purposes, in this case test() would probably be enough.

No match, because of the capital J:

>>> /j.*t/.test("Javascript")

false

Case insensitive test gives a positive result:

>>> /j.*t/i.test("Javascript")

true

The same test using exec() returns an array and you can access the first element as shown below:

>>> /j.*t/i.exec("Javascript")[0]

"Javascript"

String Methods that Accept Regular Expressions as Parameters

Previously in this chapter we talked about the String object and how you can use the methods indexOf() and lastIndexOf() to search within text. Using these methods you can only specify literal string patterns to search. A more powerful solution would be to use regular expressions to find text. String objects offer you this ability.

The string objects provide the following methods that accept regular expression objects as parameters:

  • match() returns an array of matches

  • search() returns the position of the first match

  • replace() allows you to substitute matched text with another string

  • split() also accepts a regexp when splitting a string into array elements

search() and match()

Let's see some examples of using the methods search() and match(). First, you create a string object.

>>> var s = new String('HelloJavaScriptWorld'),

Using match() you get an array containing only the first match:

>>> s.match(/a/);

["a"]

Using the g modifier, you perform a global search, so the result array contains two elements:

>>> s.match(/a/g);

["a", "a"]

Case insensitive match:

>>> s.match(/j.*a/i);

["Java"]

The search() method gives you the position of the matching string:

>>> s.search(/j.*a/i);

5

replace()

replace() allows you to replace the matched text with some other string. The following example removes all capital letters (it replaces them with blank strings):

>>> s.replace(/[A-Z]/g, ''),

"elloavacriptorld"

If you omit the g modifier, you're only going to replace the first match:

>>> s.replace(/[A-Z]/, ''),

"elloJavaScriptWorld"

When a match is found, if you want to include the matched text in the replacement string, you can access it using $&. Here's how to add an underscore before the match while keeping the match:

>>> s.replace(/[A-Z]/g, "_$&");

"_Hello_Java_Script_World"

When the regular expression contains groups (denoted by parentheses), the matches of each group are available as $1 is the first group, $2 the second and so on.

>>> s.replace(/([A-Z])/g, "_$1");

"_Hello_Java_Script_World"

Imagine you have a registration form on your web page that asks for email address, username, and password. The user enters their email, and then your JavaScript kicks in and suggests the username, taking it from the email address:

>>> var email = "[email protected]";
>>> var username = email.replace(/(.*)@.*/, "$1");
>>> username;

"stoyan"

Replace callbacks

When specifying the replacement, you can also pass a function that returns a string. This gives you the ability to implement any special logic you may need before specifying the replacements.

>>> function replaceCallback(match){return "_" +match.toLowerCase();}
>>> s.replace(/[A-Z]/g, replaceCallback);

"_hello_java_script_world"

The callback function will receive a number of parameters (we ignored all but the first one in the example above):

  • The first parameter is the match

  • The last is the string being searched

  • The one before last is the position of the match

  • The rest of the parameters contain any strings matched by any groups in your regex pattern

Let's test this. First, let's create a variable to store the whole arguments array passed to the callback function:

>>> var glob;

Next, we'll define a regular expression that has three groups and matches email addresses in the format [email protected]:

>>> var re = /(.*)@(.*).(.*)/;

Finally, we’ll define a callback function that stores the arguments in glob and then returns the replacement:

var callback = function(){
  glob = arguments;
  return arguments[1] + ' at ' + arguments[2] + ' dot ' + arguments[3];
}

We can then call this as follows:

>>> "[email protected]".replace(re, callback);

"stoyan at phpied dot com"

Here's what the callback function received as arguments:

>>> glob

["[email protected]", "stoyan", "phpied", "com", 0, "[email protected]"]

split()

You already know about the method split(), which creates an array from an input string and a delimiter string. Let's take a string of comma-separated values and split it:

>>> var csv = 'one, two,three ,four';
>>> csv.split(','),

["one", " two", "three ", "four"]

Because the input string has some inconsistent spaces before and after the commas, the array result has spaces too. With a regular expression, we can fix this, using s*, which means "zero or more spaces":

>>> csv.split(/s*,s*/)

["one", "two", "three", "four"]

Passing a String When a regexp is Expected

One last thing to note is that the four methods you just saw (split(), match(), search(), and replace()) can also take strings as opposed to regular expressions. In this case the string argument is used to produce a new regex as if it was passed to new RegExp().

Example of passing a string to replace:

>>> "test".replace('t', 'r')

"rest"

The above is the same as:

>>> "test".replace(new RegExp('t'), 'r')

"rest"

When you pass a string, you cannot set modifiers as you can with a normal constructor or regex literal.

Error Objects

Errors happen, and it's good to have the mechanisms in place so that your code can realize that there has been an error condition and recover from it in a graceful manner. JavaScript provides the statements try, catch, and finally to help you deal with errors. If an error occurs, an error object is thrown. Error objects are created by using one of these built-in constructors: EvalError, RangeError, ReferenceError, SyntaxError, TypeError, and URIError. All of these constructors inherit from the Error object.

Let's just cause an error and see what happens. An example of an error will be trying to call a function that doesn't exist. Type this into the Firebug console:

>>> iDontExist();

You'll get something like this:

Error Objects

In the bottom right corner, instead of Firebug's usual icon, you'll see:

Error Objects

If you open Firefox's error console (Tools | Error console), you'll see:

Error Objects

The display of errors can vary greatly between browsers and other host environments. In Internet Explorer you might see something like this in the lower left corner of the window:

Error Objects

If you double-click the message, you can get some more information:

Error Objects

Depending on your browser's configuration, you might not even notice that an error occurred. However, you cannot assume that all of your users have disabled the display of errors and it is your responsibility to ensure an error-free experience for them. The error above propagated to the user because the code didn't try to trap (catch) this error, it didn't expect the error and was unprepared for handling it. Fortunately, it's really easy to trap the error. All you need is the try statement, followed by a catch.

This code will not cause any of the error displays from the screenshots above

try {
  iDontExist();
} catch (e){
  // do nothing
}

Here you have:

  • The try statement, followed by a block of code

  • catch statement, followed by a variable name in parentheses and another block of code

There is also an optional finally statement, not used in this example, which is executed regardless of whether there was an error or not.

In the example above, the code block that follows the catch didn't do anything, but this is the place where you put the code that will recover from the error, or at least give some feedback to the user that you application is aware that there was a special condition.

The variable e in the parentheses after the catch statement contains an error object. Like any other object, it contains some useful properties and methods. Unfortunately, different browsers implement these methods and properties differently, but there are two properties that are consistently implemented—e.name and e.message.

Let's try this code now:

try {
  iDontExist();
} catch (e){
  alert(e.name + ': ' + e.message);
} finally {
  alert('Finally!'),
}

This will present an alert() showing e.name and e.message and then another alert() saying Finally!.

In Firefox, the first alert will say ReferenceError: iDontExist is not defined. In Internet Explorer it will be TypeError: Object expected. This tells us two things:

  • e.name contains the name of the constructor that was used to create the error object.

  • Because the error objects are not consistent across host environments (browsers), it would be somewhat tricky to have your code act differently depending on the type of error (the value of e.name).

You can also create error objects yourself using new Error() or any of the other error constructors, and then let the JavaScript engine know that there's an erroneous condition, using the throw statement.

For example, imagine a scenario where you call maybeExists() function and after that make some calculations. You want to trap all errors in a consistent way, no matter whether the error is that maybeExists() doesn't exist or that your calculations found a problem. Consider this code:

try {
  var total = maybeExists();
  if (total === 0) {
    throw new Error('Division by zero!'),
  } else {
    alert(50 / total);
  }
} catch (e){
  alert(e.name + ': ' + e.message);
} finally {
  alert('Finally!'),
}

This code will alert() different messages, depending on whether or not maybeExists() is defined and the values it returns:

  • If maybeExists() doesn't exist, you get ReferenceError: maybeExists() is not defined in Firefox and TypeError: Object expected in IE

  • If maybeExists() returns 0, you'll get Error: Division by zero!

  • If maybeExists() returns 2, you'll get an alert that says 25

In all cases, there will be a second alert that says Finally!

Inste ad of throwing a generic error throw new Error('Division by zero!'), you can be more specific if you choose to, for example, by throwing throw new RangeError('Division by zero!'). Alternatively, you don't need any constructor; you can simply throw a normal object:

throw {
  name: "MyError",
  message: "OMG! Something terrible has happened"
}

Summary

In Chapter 2, you saw that there are five primitive data types (number, string, boolean, null, and undefined) and we also said that everything that is not a primitive piece of data is an object. Now you also know that:

  • Objects are like arrays but you specify the keys.

  • Objects contain properties.

  • Some of the properties can be functions (functions are data, var f = function(){};). Properties that are functions are also called methods.

  • Arrays are actually objects with predefined numeric properties and a length property.

  • Array objects have a number of useful methods (such as sort() or slice()).

  • Functions are also objects and they have properties (such as length and prototype) and methods (such as call() and apply()).

Regarding the five primitive data types, apart from undefined (which is essentially nothing) and null (which is also an object), the other three have corresponding constructor functions: Number(), String(), and Boolean(). Using these, you can create objects, called wrapper objects, which contain some useful methods for working with primitive data elements.

Number(), String(), and Boolean() can be invoked:

  • With the new operator—to create new objects

  • Without new—to convert any value to a corresponding primitive data type

Other built-in constructor functions you're now familiar with include: Object(), Array(), Function(), Date(), RegExp(), and Error(). You are also familiar with Math, which is not a constructor.

Now you can see how objects have a central role in the JavaScript programming, as pretty much everything is an object or can be wrapped into an object.

Finally, let's wrap up the literal notations you're now familiar with.

Name

literal

constructor

Example

Object

{}

new Object()

{prop: 1}

Array

[]

new Array()

[1,2,3,'test']

regular expression

/pattern/modifiers

new RegExp('pattern', 'modifiers')

/java.*/img

Exercises

  1. Look at this code:

    function F() {
      function C() { 
        return this;
      } 
      return C();
    } 
    var o = new F(); 

    The value of this refers to the global object or the object o?

  2. What's the result of executing this piece of code?

    function C(){ 
      this.a = 1;
      return false; 
    }
    console.log(typeof new C());
  3. What's the result of executing the following piece of code?

    >>> c = [1, 2, [1, 2]];
    >>> c.sort();
    >>> c.join('--'), 
    >>> console.log(c); 
  4. Imagine the String() constructor didn't exist. Create a constructor function MyString() that acts like String() as closely as possible. You're not allowed to use any built-in string methods or properties, and remember that String() doesn't exist. You can use this code to test your constructor:

    >>> var s = new MyString('hello'),
    >>> s.length;

    5

    >>> s[0];

    "h"

    >>> s.toString();

    "hello"

    >>> s.valueOf();

    "hello"

    >>> s.charAt(1);

    "e"

    >>> s.charAt('2'),

    "l"

    >>> s.charAt('e'),

    "h"

    >>> s.concat(' world!'),

    "hello world!"

    >>> s.slice(1,3);

    "el"

    >>> s.slice(0,-1);

    "hell"

    >>> s.split('e'),

    ["h", "llo"]

    >>> s.split('l'),

    ["he", "", "o"]

    Note

    You can use a for-in to loop through the input string, treating it as an array.

  5. Update your MyString() constructor to include a reverse()method.

    Note

    Try to leverage the fact that arrays have a reverse() method.

  6. Imagine Array() doesn't exist and the array literal notation doesn't exist either. Create a constructor called MyArray() that behaves as close to Array() as possible. Test with this code:

    	>>> var a = new MyArray(1,2,3,"test");
    	>>> a.toString();

    "1,2,3,test"

    	>>> a.length;

    4

    	>>> a[a.length - 1]

    "test"

    	>>> a.push('boo'),

    5

    	>>> a.toString();

    "1,2,3,test,boo"

    	>>> a.pop();

    [1, 2, 3, "test"]

    	>>> a.toString();

    "1,2,3,test"

    	>>> a.join(',')

    "1,2,3,test"

    	>>> a.join(' isn't ')

    "1 isn't 2 isn't 3 isn't test"

    If you found this exercise amusing, don't stop with the join(); go on with as many methods as possible.

  7. Imagine Math didn't exist. Create a MyMath object that also provides some additional methods:

    • MyMath.rand(min, max, inclusive)—generates a random number between min and max, inclusive if inclusive is true (default)

    • MyMath.min(array)—returns the smallest number in a given array

    • MyMath.max(array)—returns the largest number in a given array

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.255.134