Objects - Object-Oriented JavaScript Second Edition (2013)

Object-Oriented JavaScript Second Edition (2013)

Chapter 4. Objects

Now that you've mastered JavaScript's primitive data types, arrays, and functions, it's time to make true to the promise of the book title and talk about 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, Primitive Data Types, Arrays, Loops, and Conditions, an array is just a list of values. Each value has an index (a numeric key) that starts from zero and increments 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 shown as follows:

Key

Value

0

red

1

blue

2

yellow

3

purple

An object is similar to an array, but with the difference that you define the keys yourself. You're not limited to using only numeric indexes and you 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 refers to 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 in key: value

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

var hero = {occupation: 1};

var hero = {"occupation": 1};

var hero = {'occupation': 1};

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

· If the property name is one of the reserved words in JavaScript (see Appendix A, Reserved Words)

· If it contains spaces or special characters (anything other than letters, numbers, and the _ and $ characters)

· If it starts with a number

In other words, if the name you have chosen for a property is not a valid name for a variable in JavaScript, then you need to wrap it in quotes.

Have a look at this bizarre-looking object:

var o = {

$omething: 1,

'yes or no': 'yes',

'!@#$%^&*': true

};

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

Later in this chapter, you'll 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, and members

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's just the terminology that people are used to, likely from other programming languages.

A property of an object can point to a function, because functions are just data. Properties that point to functions are also called methods. In the following example, talk is a method:

var dog = {

name: 'Benji',

talk: function () {

alert('Woof, woof!');

}

};

As you have seen in the previous chapter, it's also possible to store functions as array elements and invoke them, but you'll not see much code like this in practice:

> var a = [];

> a[0] = function (what) { alert(what); };

> a[0]('Boo!');

You can also see people using the word members to refer to properties of an object, most often when it doesn't matter if the property is a function or not.

Hashes and associative arrays

In some programming languages, there is a distinction between:

· A regular array, also called an indexed or enumerated array (the keys are numbers)

· An associative array, also called a hash or a dictionary (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 an object's properties

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

· Using the 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 the hero object again:

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"

Using the square brackets notation:

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

"Heller"

It works even if you mix both:

> book.author['lastname'];

"Heller"

> book['author'].lastname;

"Heller"

Another case where you need square brackets is when the name of the property you need to access is not known beforehand. During runtime, it's dynamically stored in a variable:

> var key = 'firstname';

> book.author[key];

"Joseph"

Calling an object's methods

You know a method is just a property that happens to be a function, so you access methods 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: you just add parentheses after the method name, which effectively says "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:

> hero['say']();

This is not a common practice unless the method name is not known at the time of writing code, but is instead defined at runtime:

var method = 'say';

hero[method]();

Tip

Best practice tip: no quotes (unless you have to)

Use the dot notation to access methods and properties and don't quote properties in your object literals.

Altering properties/methods

JavaScript allows you to alter the 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 object without properties is shown as follows:

> var hero = {};

Tip

A "blank" object

In this section, you started with a "blank" object, var hero = {};. Blank is in quotes because this object is not really empty and useless. Although at this stage it has no properties of its own, it has already inherited some. You'll learn more about own versus inherited properties later. So, an object in ES3 is never really "blank" or "empty". In ES5 though, there is a way to create a completely blank object that doesn't inherit anything, but let's not get ahead too much.

Accessing a non-existing property is shown as follows:

> typeof hero.breed;

"undefined"

Adding two 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 find the deleted name property:

> hero.sayName();

"undefined"

Tip

Malleable objects

You can always change any object at any time, such as adding and removing properties and changing their values. But, there are exceptions to this rule. A few properties of some built-in objects are not changeable (for example, Math.PI, as you'll see later). Also, ES5 allows you to prevent changes to objects; you'll learn more about it in Appendix C, Built-in Objects.

Using the this value

In the previous example, the sayName() method 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 the 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're 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"

A 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"

Tip

Best practice

By convention, you should capitalize the first letter of your constructor functions so that you have a visual clue that this is not intended to be called as a regular 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 doesn't give you the expected result.

> var h = Hero('Leonardo');

> typeof h;

"undefined"

What happened here? There is no new operator, so a new object was not created. 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 returnsundefined, which gets assigned to h.

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

The global object

You have already learned a bit about global variables (and how you should avoid them). You also know that JavaScript programs run inside a host environment (the browser for example). Now that you know about objects, it's time for the whole truth: the host environment provides a global object and all global variables are accessible as properties of the global object.

If your host environment is the web browser, the global object is called window. Another way to access the global object (and this is also true in most other environments) is to use this outside a constructor function, for example in the global program code outside any function.

As an illustration, you can declare a global variable outside 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

· As a property of the global object referred to as this:

· > var a = 1;

· > window.a;

· 1

· > this.a;

· 1

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 the properties set to 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;

TypeError: Cannot read property 'name' of undefined

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 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 built-in global functions you have seen in Chapter 3, Functions, can also be invoked as methods of the window object. So, the following two calls have the same result:

> parseInt('101 dalmatians');

101

> window.parseInt('101 dalmatians');

101

And, when outside a function called as a constructor (with new), also:

> this.parseInt('101 dalmatians');

101

The 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;

function Hero(name) {

this.name = 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 (there is more about this later in this chapter):

> var o = {};

> o.constructor;

function Object() { [native code] }

> typeof o.constructor;

"function"

The instanceof operator

With 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;

true

> o instanceof Object;

true

Note that you don't put parentheses after the function name (you don't use h instanceof Hero()). This is because you're not invoking this function, but just referring to it by name, as with 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 to create objects without new. You can have a function that does a bit of 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() function:

> var o = factory('one');

> o.name;

"one"

> o.constructor;

function Object() { [native code] }

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.

If you think about how objects are created inside constructor functions, you can imagine that a variable called this is defined at the top of the function and then returned at the end. It's as if something like this happens:

function C() {

// var this = {}; // pseudo code, you can't do this

this.a = 1;

// return this;

}

Passing objects

When you assign an object to a different variable or pass it to a function, you only pass a reference to that object. Consequently, if you make a change to the reference, you're 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 mycopy = original;

> mycopy.howmany;

1

> mycopy.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 returns false.

Let's create two objects that look the same:

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

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

Comparing them returns 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 the mydog variable's properties will change the benji variable's properties). 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 WebKit console

Before diving into the built-in objects in JavaScript, let's quickly say a few words about working with objects in the WebKit 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 an arrow pointing to the word Object.

The object is clickable and expands to show you a list of all of the properties of the object. If a property is also an object, there is an arrow next to it too, so you can expand this as well. This is handy as it gives you an insight into exactly what this object contains.

Objects in the WebKit console

You can ignore __proto__ for now; there's more about it in the next chapter.

console.log

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

console.log

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 debugging 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, you came across the Object() constructor function. It's 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 a few 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: These are Object, Array, Function, Boolean, Number, and String. These objects correspond to the different data types in JavaScript. There is a data wrapper object for every different value returned by typeof (discussed in Chapter 2,Primitive Data Types, Arrays, Loops, and Conditions), with the exception of "undefined" and "null".

· Utility objects: These are Math, Date, and RegExp, and can come in handy.

· Error objects: These include 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, Built-in Objects.

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

Object

Object 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();

As mentioned before, an "empty" (or "blank") object is not completely useless because it already contains several inherited methods and properties. In this book, "empty" means an object like {} that has no properties of its own other than the ones it automatically gets. Let's see a few of the properties that even "blank" objects already have:

· The o.constructor property returns a reference to 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 toString()method will be called behind the scenes. These two lines produce the same result:

> alert(o);

> alert(o.toString());

Another type of string context is the string concatenation. If you try to concatenate an object with a string, the object's toString() method is 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 returns 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 that 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 is considered to be the length of the array:

> var a2 = new Array(5);

> a2;

[undefined x 5]

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 [1, 2, 3];

"object"

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

> var a = [1, 2, 3, 'four'];

> a.toString();

"1,2,3,four"

> a.valueOf();

[1, 2, 3, "four"]

> a.constructor;

function Array() { [native code] }

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 that contains the number of elements in the array

· They have more 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 array a and the empty object o:

> 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, while it ignores 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 makes room for additional elements. If you try to access these non-existing elements, you'll get the value undefined:

> a.length = 5;

5

> a;

[1, undefined x 4]

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

> a.length = 2;

2

> a;

[1, undefined x 1]

A few array methods

In addition to the methods inherited from the parent Object, array objects also have specialized methods for working with arrays, such as sort(), join(), and slice(), among others (see Appendix C, Built-in Objects, for the full list).

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

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

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

push() returns the length of the changed array, whereas pop() returns the removed element:

> 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 it. In the next example, after the sort, both a and b point to the same array:

> var b = a.sort();

> b;

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

> a === b;

true

The join() method returns a string containing the values of all the elements in the array glued 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"

The slice() method returns a piece of the array without modifying the source array. The first parameter to slice() is the start index (zero-based) 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"]

The splice() method modifies the source array. It removes a slice, returns it, and optionally fills the gap with new elements. The first two parameters define the start index and length (number of elements) 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() that allows for an alternative (but not necessarily recommended) way to create a function.

The following example shows three ways to define a function:

> function sum(a, b) { // function declaration

return a + b;

}

> sum(1, 2);

3

> var sum = function (a, b) { // function expression

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 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]

Tip

Best practice

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

Properties of function objects

Like any other object, functions have a constructor property that contains a reference to the Function() constructor function. This is true no matter which syntax you used to create the function.

> function myfunc(a) {

return a;

}

> myfunc.constructor;

function Function() { [native code] }

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

> function myfunc(a, b, c) {

return true;

}

> myfunc.length;

3

Prototype

One of the most widely used properties of function objects is the prototype property. You'll see this property discussed in detail in the next chapter, but for now, let's just say:

· The prototype property of a function object points to another object

· Its benefits shine only 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. Take a simple object that has a property name and a method say().

var ninja = {

name: 'Ninja',

say: function () {

return 'I am a ' + this.name;

}

};

When you create a function (even one without a body), you can verify that it automatically has a prototype property that points to a new object.

> function F() {}

> typeof F.prototype;

"object"

It gets interesting when you modify the prototype property. You can add properties to it or you can replace the default object with any other object. Let's assign ninja to the prototype.

> F.prototype = ninja;

Now, and here's where the magic happens, using the function F() as a constructor function, you can create a new object, baby_ninja, which will have access to the properties of F.prototype (which points to ninja) as if it were its own.

> var baby_ninja = new F();

> baby_ninja.name;

"Ninja"

> baby_ninja.say();

"I am a Ninja"

There will be much more on this topic later. In fact, the whole next chapter is about the prototype property.

Methods of function objects

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 string [native code] instead of the body of the function.

> parseInt.toString();

"function parseInt() { [native code] }"

As you can see, you can use toString() to differentiate between native methods and developer-defined ones.

Note

The behavior of the function's toString() is environment-dependent, and it does differ among browsers in terms of spacing and new lines.

Call and apply

Function objects have call() and apply() methods. You can use them to invoke a function and pass any arguments to it.

These methods also 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.

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

my_obj likes the some_obj object'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? You 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() is invoked, the references to the this value that it contains point to my_obj. This way, this.name doesn'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 you pass null, the global object is 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 previous example, 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 have seen how, from inside a function, you have access to something called arguments, which contains the values of all the parameters passed to the function:

> function f() {

return arguments;

}

> f(1, 2, 3);

[1, 2, 3]

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

However, you can convert arguments to an array and benefit from all the array goodies. Here's what you can do, practicing your newly-learned call() method:

> function f() {

var args = [].slice.call(arguments);

return args.reverse();

}

> f(1, 2, 3, 4);

[4, 3, 2, 1]

As you can see, you can borrow slice() using [].slice or the more verbose Array.prototype.slice.

Inferring object types

You can see that you have this array-like arguments object looking so much like an array object. How can you reliably tell the difference between the two? Additionally, typeof returns object when used with arrays. Therefore, how can you tell the difference between an object and an array?

The silver bullet is the Object object's toString() method. It gives you the internal class name used to create a given object.

> Object.prototype.toString.call({});

"[object Object]"

> Object.prototype.toString.call([]);

"[object Array]"

You have to call the original toString() method as defined in the prototype of the Object constructor. Otherwise, if you call the Array function's toString(), it will give you a different result, as it's been overridden for the specific purposes of the array objects:

> [1, 2, 3].toString();

"1,2,3"

This is the same as:

> Array.prototype.toString.call([1, 2, 3]);

"1,2,3"

Let's have some more fun with toString(). Make a handy reference to save typing:

> var toStr = Object.prototype.toString;

Differentiate between an array and the array-like object arguments:

> (function () {

return toStr.call(arguments);

}());

"[object Arguments]"

You can even inspect DOM elements:

> toStr.call(document.body);

"[object HTMLBodyElement]"

Boolean

Your journey through the built-in objects in JavaScript continues, and the next three are fairly straightforward; they merely wrap the primitive data types Boolean, number, and string.

You already know a lot about Booleans from Chapter 2, Primitive Data Types, Arrays, Loops, and Conditions. Now, let's meet the Boolean() constructor:

> var b = new Boolean();

It's 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 and customized):

> 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, when called as a normal function without new, converts non-Booleans to Booleans (which is like using a double negation !!value):

> Boolean("test");

true

> Boolean("");

false

> Boolean({});

true

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

> Boolean(new Boolean(false));

true

This can be confusing, and since Boolean objects don't offer any special methods, it's best to just stick with regular primitive Boolean values.

Number

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

· A constructor function (with new) to create objects.

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

· > 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 also have properties. The Number() function has constant built-in properties that 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, Built-in Objects, for more 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 first. In such cases, the number object is created (and destroyed) for you behind the scenes:

> (12345).toExponential();

"1.2345e+4"

Like all objects, number objects also provide the toString() method. When used with number objects, this method accepts an optional radix parameter (10 being 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

You can use the String() constructor function to create string objects. String objects provide convenient methods for text manipulation.

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 similar to an array of characters. String objects have an indexed property for each character (introduced in ES5, but long supported in many browsers except old IEs) 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 primitive 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 also offers you the syntax to treat primitive strings as objects (just like you saw already with primitive numbers).

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

> "potato".length;

6

> "tomato"[0];

"t"

> "potatoes"["potatoes".length - 1];

"s"

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 (because all objects are 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(1);

"1"

If you pass an object to String(), this object's toString() method will be called first:

> String({p: 1});

"[object Object]"

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

"1,2,3"

> String([1, 2, 3]) === [1, 2, 3].toString();

true

A few methods of string objects

Let's experiment with a few of the methods you can call on string objects (see Appendix C, Built-in Objects, for the full list).

Start off by creating a string object:

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

toUpperCase() and toLowerCase()  transforms 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 (treating a string as 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 also search for strings, not only characters, and the search is case sensitive:

> s.indexOf('Couch');

0

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

> s.indexOf('couch');

-1

For a case-insensitive search, you can transform the string to lowercase first and then search:

> 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 converts 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) to both methods, 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"

There's also the non-standard method substr(), but you should try to avoid it in favor of substring().

The split() method creates an array from the string using another 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 preceding methods discussed return new primitive strings, none of them modify the source string. After all the method calls listed previously, the initial string is still the same:

> s.valueOf();

"Couch potato"

You have seen how to use indexOf() and lastIndexOf() to search within strings, but there are more powerful methods (search(), match(), and replace()) that take regular expressions as parameters. You'll see these later in the RegExp() constructor function.

At this point, you're done with all of the data wrapper objects, so let's move on to the utility objects Math, Date, and RegExp.

Math

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

The Math object's properties are constants, so you can't change their values. Their names are all in uppercase to emphasize the difference between them and a normal property (similar to the constant properties of the Number() constructor). Let's see a few of these constant properties:

· The constant π:

· > Math.PI;

· 3.141592653589793

· Square root of 2:

· > Math.SQRT2;

· 1.4142135623730951

· Euler's constant:

· > 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 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, Built-in Objects).

Generating random numbers:

> Math.random();

0.3649461670235814

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

> 100 * Math.random();

For numbers between any two values, 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 the following rounding methods:

· floor() to round down

· ceil() to round up

· 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 min() and max() methods. 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 (a value between 1 and 12):

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

The Math object also provides 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.

For example, to calculate 2 to the power of 8:

> Math.pow(2, 8);

256

And to calculate the 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

Following is an object instantiated with today's date/time:

> new Date();

Wed Feb 27 2013 23:49:28 GMT-0800 (PST)

The console displays the result of the toString() method called on the date object, so you get this long string Wed Feb 27 2013 23:49:28 GMT-0800 (PST) as a representation of the date object.

Here are a few examples of using strings to initialize a date object. Note how many different formats you can use to specify the date:

> new Date('2015 11 12');

Thu Nov 12 2015 00:00:00 GMT-0800 (PST)

> new Date('1 1 2016');

Fri Jan 01 2016 00:00:00 GMT-0800 (PST)

> new Date('1 mar 2016 5:30');

Tue Mar 01 2016 05:30:00 GMT-0800 (PST)

The Date constructor can figure out a date from different strings, but this is not really a reliable way of defining a precise date, for example when passing user input to the constructor. 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(2015, 0, 1, 17, 05, 03, 120);

Tue Jan 01 2015 17:05:03 GMT-0800 (PST)

Passing date and hour:

> new Date(2015, 0, 1, 17);

Tue Jan 01 2015 17:00:00 GMT-0800 (PST)

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

> new Date(2016, 1, 28);

Sun Feb 28 2016 00:00:00 GMT-0800 (PST)

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

> new Date(2016, 1, 29);

Mon Feb 29 2016 00:00:00 GMT-0800 (PST)

> new Date(2016, 1, 30);

Tue Mar 01 2016 00:00:00 GMT-0800 (PST)

Similarly, December 32nd becomes January 1st of the next year:

> new Date(2012, 11, 31);

Mon Dec 31 2012 00:00:00 GMT-0800 (PST)

> new Date(2012, 11, 32);

Tue Jan 01 2013 00:00:00 GMT-0800 (PST)

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

> new Date(1357027200000);

Tue Jan 01 2013 00:00:00 GMT-0800 (PST)

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

> Date();

Wed Feb 27 2013 23:51:46 GMT-0800 (PST)

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

Wed Feb 27 2013 23:51:52 GMT-0800 (PST)

> typeof Date();

"string"

> typeof new Date();

"object"

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(2015, 1, 1);

> d.toString();

Sun Feb 01 2015 00:00:00 GMT-0800 (PST)

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

> d.setMonth(2);

1425196800000

> d.toString();

Sun Mar 01 2015 00:00:00 GMT-0800 (PST)

Getting the month:

> d.getMonth();

2

In addition to all the methods of date objects, there are also two methods (plus one more added in ES5) that are properties of the Date() function/object. These do not need a date object; they work just like the Math object'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 11, 2018');

1515657600000

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

> Date.UTC(2018, 0, 11);

1515628800000

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(2018, 0, 11));

Wed Jan 10 2018 16:00:00 GMT-0800 (PST)

> new Date(2018, 0, 11);

Thu Jan 11 2018 00:00:00 GMT-0800 (PST)

The ES5 addition to the Date constructor is the method now(), which returns the current timestamp. It provides a more convenient way to get the timestamp instead of using the getTime() method on a date object as you would in ES3:

> Date.now();

1362038353044

> Date.now() === new Date().getTime();

true

You can think of the internal representation of the date being an integer timestamp and all other methods being "sugar" on top of it. So, it makes sense that the valueOf() is a timestamp:

> new Date().valueOf();

1362418306432

Also dates cast to integers with the + operator:

> +new Date();

1362418318311

Calculating birthdays

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

> var d = new Date(2016, 5, 20);

> d.getDay();

1

Starting the count from 0 (Sunday), 1 means Monday. Is that so?

> d.toDateString();

"Mon Jun 20 2016"

OK, good to know, but Monday is not necessarily the best day for a party. So, how about a loop that shows how many times June 20th is a Friday from year 2016 to year 3016, or better yet, let's see the distribution of all the days of the week. After all, with all the progress in DNA hacking, we're all going to be alive and kicking in 3016.

First, let's initialize an array with seven elements, one for each day of the week. These will be used as counters. Then, as a loop goes up to 3016, let's increment the counters:

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

The loop:

for (var i = 2016; i < 3016; i++) {

stats[new Date(i, 5, 20).getDay()]++;

}

And the result:

> stats;

[140, 146, 140, 145, 142, 142, 145]

142 Fridays and 145 Saturdays. Woo-hoo!

RegExp

Regular expressions provide a powerful way to search and manipulate 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 used

The pattern can be as simple as literal text to be matched verbatim, but that's rare, and in such cases you're better off using indexOf(). Most of the time, the pattern is more complex and could be difficult to understand. Mastering regular expression's patterns is a large topic, which won't be discussed in full detail 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, Regular Expressions, to help you when you're 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 notation:

> var re = /j.*t/;

In the preceding example, 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 quoted when passed to a RegExp() constructor.

Properties of RegExp objects

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: When the match is case insensitive, the defaults to false (meaning the default is a case sensitive match).

· multiline: Search matches that may span over more than one line default to false.

· lastIndex: The position at which to start the search; this defaults to 0.

· source: Contains the regexp pattern.

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

The first three items in the preceding list 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 property 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 RegExp objects

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 to validate data, in this case, test() should be enough.

In the following example, there is no match because of the capital J:

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

false

A 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 arguments

Previously in this chapter, you learned about string objects and how you can use the indexOf() and lastIndexOf()methods 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.

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 search() and match() methods. 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"]

A case insensitive match is as follows:

> 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 for 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 an e-mail address, username, and password. The user enters their e-mail, and then your JavaScript kicks in and suggests the username, taking it from the e-mail address:

> var email = "stoyan@phpied.com";

> 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 receives a number of parameters (the previous example ignores all but the first one):

· 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 entire arguments array passed to the callback function:

> var glob;

Next, define a regular expression that has three groups and matches e-mail addresses in the format something@something.something:

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

Finally, let's 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];

};

Now perform a test:

> "stoyan@phpied.com".replace(re, callback);

"stoyan at phpied dot com"

Here's what the callback function received as arguments:

> glob;

["stoyan@phpied.com", "stoyan", "phpied", "com", 0,

"stoyan@phpied.com"]

split()

You already know about the split() method, 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 happens to have random inconsistent spaces before and after the commas, the array result has spaces too. With a regular expression, you 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 that you have just seen (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().

An example of passing a string to replace is shown as follows:

> "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 the way you do with a normal constructor or regex literal. There's a common source of errors when using a string instead of a regular expression object for string replacements, and it's due to the fact that the gmodifier is false by default. The outcome is that only the first string is replaced, which is inconsistent with most other languages and a little confusing. For example:

> "pool".replace('o', '*');

"p*ol"

Most likely, you want to replace all occurrences:

> "pool".replace(/o/g, '*');

"p**l"

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 can 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 Error.

Let's just cause an error and see what happens. What's a simple way to cause an error? Just call a function that doesn't exist. Type this into the console:

> iDontExist();

You'll get something like this:

Error objects

The display of errors can vary greatly between browsers and other host environments. In fact, most recent browsers tend to hide the errors from the users. 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 previous error propagated to the user because the code didn't try to trap (catch) this error. The code didn't expect the error and was not prepared to handle it. Fortunately, it's trivial to trap the error. All you need is the try statement followed by a catch statement.

This code hides the error from the user:

try {

iDontExist();

} catch (e) {

// do nothing

}

Here you have:

· The try statement followed by a block of code

· The catch statement followed by a variable name in parentheses and another block of code

There can be an optional finally statement (not used in this example) followed by a block of code, which is executed regardless of whether there was an error or not.

In the previous example, the code block that follows the catch statement didn't do anything, but this is the place where you put the code that can help recover from the error, or at least give feedback to the user that your 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 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 and Chrome, 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 the maybeExists() function and after that make 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!.

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

throw {

name: "MyError",

message: "OMG! Something terrible has happened"

}

This gives you cross-browser control over the error name.

Summary

In Chapter 2, Primitive Data Types, Arrays, Loops, and Conditions, 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.

· Properties can be functions (functions are data; remember var f = function () {};). Properties that are functions are also called methods.

· Arrays are actually objects with predefined numeric properties and an auto-incrementing length property.

· Array objects have a number of convenient 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 and null, the other three have the corresponding constructor functions: Number(), String(), and Boolean(). Using these, you can create objects, called wrapper objects, which contain methods for working with primitive data elements.

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

· With the new operator—to create new objects

· Without the new operator—to convert any value to the corresponding primitive data type

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

Now you can see how objects have a central role in JavaScript programming, as pretty much everything is an object or can be wrapped by 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:

2. function F() {

3. function C() {

4. return this;

5. }

6. return C();

7. }

var o = new F();

Does the value of this refer to the global object or the object o?

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

9. function C(){

10. this.a = 1;

11. return false;

12.}

console.log(typeof new C());

13. What's the result of executing the following piece of code?

14.> c = [1, 2, [1, 2]];

15.> c.sort();

16.> c.join('--');

> console.log(c);

17. 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:

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

19.> s.length;

20. 5

21.> s[0];

22. "h"

23.> s.toString();

24. "hello"

25.> s.valueOf();

26. "hello"

27.> s.charAt(1);

28. "e"

29.> s.charAt('2');

30. "l"

31.> s.charAt('e');

32. "h"

33.> s.concat(' world!');

34. "hello world!"

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

36. "el"

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

38. "hell"

39.> s.split('e');

40. ["h", "llo"]

41.> s.split('l');

42. ["he", "", "o"]

Note

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

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

Note

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

44. 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 it with the following code:

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

46.> a.toString();

47. "1,2,3,test"

48.> a.length;

49. 4

50.> a[a.length - 1];

51. "test"

52.> a.push('boo');

53. 5

54.> a.toString();

55. "1,2,3,test,boo"

56.> a.pop();

57. "boo"

58.> a.toString();

59. "1,2,3,test"

60.> a.join(',');

61. "1,2,3,test"

62.> a.join(' isn\'t ');

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

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

64. Imagine Math didn't exist. Create a MyMath object that also provides 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