Learn JavaScript: Novice to Ninja

Chapter 5: Objects

Everything in JavaScript is either one of the six primitive data types we met in Chapter 2 (strings, numbers, booleans, symbols, undefined, and null) or an object. We’ve actually met some objects already; arrays in Chapter 3 and functions in Chapter 4 are both objects, although these are built-in objects that are part of the language. In this chapter we’re going to look at user-defined objects, as well as some of the other built-in objects.

In this chapter, we’ll cover the following topics:

  • Object literals
  • Adding properties to objects
  • Object methods
  • JSON
  • The Math object
  • The Date object
  • The RegExp object
  • Project ― we’ll create quiz and question objects and ask random questions

Object Literals

An object in JavaScript is a self-contained set of related values and functions. They act as a collection of named properties that map to any JavaScript value such as strings, numbers, booleans, arrays and functions. If a property’s value is a function, it is known as amethod.

One way to think about an object is that it’s like a dictionary where you look up a property name and see a value. It’s like a database of values (in fact, some databases use JavaScript objects to store information). JavaScript objects are similar to a hash or associative array in other programming languages (or even a JavaScript map ). They are, however, much more flexible, as they can be employed to encapsulate code that can be reused throughout a program. They can also inherit properties from other objects in a similar way to object-oriented languages (we’ll cover how to do this in Chapter 11).

Objects are often used to keep any related information and functionality together in the same place. For example, if you wrote functions that found the perimeter and area of a square, you might want to group them together as methods of the same object that also included a length property.

A Super Example

An object literal is an object that is created directly in the language by wrapping all its properties and methods in curly braces {} . Object literals are a distinguishing feature of the JavaScript language, as they allow objects to be created quickly without the need for defining a class. They also provide a useful way of organizing your code without polluting the global namespace.

Here is an example of an object literal that describes the Man of Steel:

const superman = {    name: 'Superman',    'real name': 'Clark Kent',    height: 75,    weight: 235,    hero: true,    villain: false,    allies: ['Batman','Supergirl','Superboy'],    fly() {        return 'Up, up and away!';    }};

Each property is a key-value pair, separated by commas. In the example, the first property is called name and its value is 'Superman' , while the fly() property is a method, as its value is a function, signified by the parentheses placed after it. If there were further methods after this, they would be comma-separated as well.

If a property’s name doesn’t follow the rules for naming variables described in Chapter 2, it needs to be quoted. The property 'real name' in the example above needs to be quoted because it contains a space.

Differing Naming Conventions

It’s very uncommon to use property and method names that don’t follow the rules for naming variables. In a real-world app, it’s likely the "real name" property would actually be named real_name or realName .

All objects are mutable at any time when a program is running. This means that its properties and methods can be changed or removed, and new properties and methods can be added to the object, even if it was declared using const .

Creating Objects

To create an object literal, simply enter a pair of curly braces. The following example creates an empty object that is assigned to the variable spiderman :

const spiderman = {};

It’s also possible to create an object using a constructor function. This example will also create an empty object:

const spiderman = new Object();

This method is not recommended, however, and the object literal notation is the preferred way of creating objects. The obvious reason is because it requires less typing and provides a concise way of initializing an object and its properties in one statement.

ES6 provided a shorthand method of creating objects if a property key is the same as a variable name that the property value is assigned to:

const name = 'Iron Man';const realName = 'Tony Stark';// long wayconst ironMan = { name: name, realName: realName };// short ES6 wayconst ironMan = { name, realName };

Accessing Properties

You can access the properties of an object using the dot notation that we’ve already seen in previous chapters. This will return the value of that property, as can be seen in the example below:

superman.name<< 'Superman'

You can also access an object’s properties using bracket notation ― the property is represented by a string inside square brackets, so needs to be placed inside single or double quotation marks:

superman['name']<< 'Superman'

Dot notation is much more common, but bracket notation has a few advantages: it’s the only way to access nonstandard property and method names that don’t follow the variable naming rules. It also lets you evaluate an expression and use it as the property key:

superman["real" + " " + "name"] // the property is built using string concatenation<< "Clark Kent"

If you try to access a property that doesn’t exist, undefined will be returned:

superman.city<< undefined

Computed Properties

The ability to create objects with computed property keys was introduced in ES6. This means that JavaScript code can be placed inside square brackets and the property key will be the return value of that code. This can be seen in the example below where the + operator is used to concatenate the strings ‘catch’ and ‘phrase’:

const hulk = { name: 'Hulk', ['catch' + 'Phrase']: 'Hulk Smash!' };

If we take a look at the hulk object, we can see the property key is named ‘catchPhrase’:

<< { name: 'Hulk', catchPhrase: 'Hulk Smash!' }

The value of a property has always been allowed to be a JavaScript expression. In the example below a ternary operator is used to return a true or false value for the hero property depending on the value of the bewitched variable:

const bewitched = true;const captainBritain = { name: 'Captain Britain', hero: bewitched ? false : true };captainBritain<< { name: 'Captain Britain', hero: false }

The new Symbol date type can also be used as a computed property key:

const name = Symbol('name');const supergirl = { [name]: 'Supergirl' };

You can access the property using the square bracket notation:

supergirl[name];<< 'Supergirl'

A new property can be added to an object using a symbol as a key if the square bracket notation is used:

const realName = Symbol('real name');supergirl[realName] = 'Kara Danvers';<< 'Kara Danvers'

The symbols used for property keys are not limited to being used by only one object – they can be reused by any other object:

const daredevil = { [name]: 'Daredevil', [realName]: 'Matt Murdoch' };

Each symbol has a unique value, which means that using them as property keys avoids any naming clashes if you mistakenly use the same value for two different property keys. This might not seem likely in the examples we’ve seen so far, but it can be a problem if you’re working with an object that has a large number of properties or if other developers are also working with the code.

Calling Methods

To call an object’s method we can also use dot or bracket notation. Calling a method is the same as invoking a function, so parentheses need to be placed after the method name:

superman.fly()<< 'Up, up and away!'superman['fly']()<< 'Up, up and away!'

Checking if Properties or Methods Exist

The in operator can be used to check whether an object has a particular property. So, for example, we can check if the superman object has a property called city using this code:

'city' in superman;<< false

Alternatively, you could also check to see if the property or method doesn’t return undefined :

superman.city !== undefined;<< false

Another way is to use the hasOwnProperty() method. As mentioned earlier, objects can inherit properties from other objects, so all objects have a method called hasOwnProperty() . This can be used to check whether an object has a property that is its own, rather than one that has been inherited from another object:

superman.hasOwnProperty('city');<< falsesuperman.hasOwnProperty('name');<< true

This method willonlyreturn any properties that belong to that particular object, whereas using in or !== undefined will return true , even if the property has been inherited from another object (inheritance is covered later in Chapter 12).

Finding all the Properties of an Object

We can loop through all of an object’s properties and methods by using a for in loop. For example, to log all the properties of the superman object to the console, we could use:

for(const key in superman) {    console.log(key + ": " + superman[key]);}<< "name: Superman"<< "real name: Clark Kent"<< "height: 75"<< "weight: 235"<< "hero: true"<< "villain: false"<< "allies: Batman,Supergirl,Superboy"<< "fly: function (){    console.log(\"Up, up and away!\");}"

In this example, we create a variable called key . We then iterate over the properties of the superman object and use key to log the property name and superman[key] to look up the value of each property.

To make sure that only an object’s own properties are returned, a quick check can be implemented beforehand:

for(const key in superman) {    if(superman.hasOwnProperty(key)){        console.log(key + ": " + superman[key]);    }}

The following methods will only iterate over an object’s own properties, so a check isn’t required to ensure that inherited properties are ignored.

The Object.keys() method will return an array of all the keys of any object that is provided as an argument. We can then iterate over this array to access all the keys of an object:

for(const key of Object.keys(superman)) {    console.log(key);}<<  name    real name    height    weight    hero    villain    allies    fly

ES2017 also adds some the Object.values() that works in the same way, but returns an array of all the object’s value:

for(const value of Object.values(superman)) {    console.log(value);}<<  Superman    Clark Kent    75    235    true    false    [ 'Batman','Supergirl','Superboy' ]    [Function: fly]

 Object.entries() is also part of ES2017 and returns an array of key-value pairs. These key-value pairs are returned in arrays, but they can be destructured and accessed individually using the following notation:

for(const [key,value] of Object.entries(superman)) {    console.log(`${key}: ${value}`);}<<  name: Superman    real name: Clark Kent    height: 75    weight: 235    hero: true    villain: false    allies: [ 'Batman','Supergirl','Superboy' ]    fly: [Function: fly]

Adding Properties

New properties and methods can be added to objects at any time in a program. This is done by simply assigning a value to the new property. For example, if we wanted to add a new city property to our superman object, we would do it like so:

superman.city = 'Metropolis';<< 'Metropolis'

Now if we take a look at the superman object, we can see that it has a city property:

superman<< { name: 'Superman',    'real name': 'Clark Kent',    height: 75,    weight: 235,    hero: true,    villain: false,    allies: [ 'Batman', 'Supergirl', 'Superboy' ],    fly: [Function: fly]     city: 'Metropolis' }

It’s important to note that properties don’t always appear in the order they were entered. An object is not an ordered list like an array, set or map, so you should never rely on the properties being in a certain order.

Changing Properties

You can change the value of an object’s properties at any time using assignment. For example, we can change the value of the "real name" property like this:

superman['real name'] = 'Kal-El';<< 'Kal-El'

We can check the update has taken place by taking a look at the object:

superman<< {'allies': ['Batman', 'Supergirl', 'Superboy'], 'city': 'Metropolis', 'fly': function (){    console.log('Up, up and away!');}, "height": 75, 'hero': true, 'name": 'Superman", 'real name': 'Kal-El", 'villain': false, 'weight': 235}

Removing Properties

Any property can be removed from an object using the delete operator. For example, if we wanted to remove the fly method from the superman object, we would enter the following:

delete superman.fly<< true

Now if we take a look at the superman object, we can see that the Man of Steel has lost his ability to fly:

superman<< {"allies": ['Batman', 'Supergirl', 'Superboy'], 'city': 'Superman', 'real name': 'Kal-El', 'villain': false, 'weight': 235}

Nested Objects

It’s even possible for an object to contain other objects. These are known as nested objects. Here’s an example of an object that contains a list of other objects. It has been assigned to the variable jla :

const jla = {    superman: { realName: 'Clark Kent' },    batman: { realName: 'Bruce Wayne' },    wonderWoman: { realName: 'Diana Prince" },    flash: { realName: 'Barry Allen' },    aquaman: { realName: 'Arthur Curry' },}

The values in nested objects can be accessed by referencing each property name in order using either dot or bracket notation:

jla.wonderWoman.realName<< "Diana Prince"jla['flash']['realName']<< "Barry Allen"

You can even mix the different notations:

jla.aquaman['realName']<< "Arthur Curry"

Objects Are Copied By Reference

An important concept to get your head around is that objects are assigned byreference. This means that if a variable is assigned to an object that already exists, it will simply point to the exact same space in memory. So any changes made usingeitherreference will affect the same object.

In the example below, we create a new object called thor to represent The Mighty Thor and make a copy of it called cloneThor :

const thor = { name: 'Thor' // more properties here};const cloneThor = thor;

The variable cloneThor now has all the same properties as the thor object. The problem is, we haven’t created a new object that is a copy of thor ; the variables cloneThor and thor both reference exactly the same object!

We can see this, if we make a change to the name property of cloneThor :

cloneThor.name = 'Clor';

Now if we check the value of the name property of the thor object, we’ll discover a problem:

thor.name<< 'Clor'

Changing the name property of cloneThor has resulted in the name property of thor changing as well. This happens because the variables thor and cloneThor both point to the same object in memory. Any changes made to either variable will affect the other.

This doesn’t happen when primitive values are used instead of objects, as can be seen in the example below:

a = 1;b = a;

At this point, both a and b will have a value of 1, but if the value of b is changed, it won’t affect the value of a :

b = 2;// check the value of a hasn't changeda<< 1

Objects as Parameters to Functions

An object literal can be passed as a parameter to a function. This is useful when there are a large number of parameters, as it allows the arguments to be provided by name and in any order. This means you don’t have to remember the order to enter them when invoking a function.

The following example shows how this can be done using a function called greet() . This accepts three parameters:

function greet({greeting,name,age}) {    return `${greeting}! My name is ${name} and I am ${age} years old.`;}

Here’s an example of how the function can be used. Notice how the order of the properties in the argument object differs from the order they are listed in the object provided as a parameter to the function:

greet({ greeting: `What's up dude`, age: 10, name: `Bart` });<< 'What\'s up dude! My name is Bart and I am 10 years old.'

We can provide default values for some of the parameters using assignment, as we saw in the last chapter. In the following example, the greeting and age parameters now have default values, but the name parameter still has to be provided as an argument, otherwise it will be set as undefined :

function greet({greeting='Hello',name,age=18}) {    return `${greeting}! My name is ${name} and I am ${age} years old.`;}

If we leave out the greeting argument it will be set to ‘Hello’, but the default values can also be overridden, as we do with the age value in the example below:

greet({ name: 'Lisa', age: 8 });<< 'Hello! My name is Lisa and I am 8 years old.'

This technique is referred to as usingnamed parametersand is often used when a function has a large amount of optional parameters.

 this

The keyword this refers to the object that it is within. It can be used inside methods to gain access to the object’s properties.

To demonstrate using this , we’ll create a dice object that has a sides property and a roll() method that returns a number between 1 and the number of sides.

Here’s the code to create our dice object:

const dice = {    sides: 6,    roll() {        return Math.floor(this.sides * Math.random()) + 1;    }}

This object has a sides property and a roll() method. Inside the roll() method we use this.sides to refer to the value of the object’s sides property.

We also use the random() and floor() methods of the Math object to return a number between 1 and the number of sides.

Let’s take it for a spin:

dice.roll();<< 5dice.roll();<< 3

If we want to change the number of sides, all we need to do is modify the sides property:

dice.sides = 20;<< 20

Now the roll() method will return a random number between 1 and 20 instead, without us having to modify it:

dice.roll();<< 12dice.roll();<< 18

Namespacing

Naming collisions occur when the same variable or function name is used for different purposes by code sharing the same scope. This might not seem likely, but imagine if you have lots of code that has been created over time – you might end up reusing a variable name without realizing. The problem becomes more likely if you use code libraries from other developers or work on code in teams, as you might choose the same name for a function as another member of the team.

A solution to this problem is to use theobject literal patternto create a namespace for groups of related functions. This is done by creating an object literal that serves as the namespace, then adding any values as properties of that object, and any functions as methods.

For example, in the last chapter we created some functions for squaring numbers and finding the mean. One of the functions used was called square() . This is quite a generic name and it wouldn’t be too far fetched to imagine a situation where a square() function also existed for drawing squares using the Canvas API (this is covered in Chapter 14). To prevent this happening, we can place all our functions inside an object, thereby creating a namespace for them. In the example below, the namespace is myMaths , which is the name of the variable the object that contains the functions has been assigned to:

const myMaths = {    square(x) {        return x * x;    },    mean(array,callback) {        if (callback) {        array.map( callback );        }         const total = array.reduce((a, b) => a + b);        return total/array.length;    }};

Now these functions need to be preceded by the namespace to be invoked:

myMaths.square(3)<< 9myStats.mean([1,2,3])<< 2

This would avoid any clashes with any other functions called square() as they would also be defined in their own namespace. For example, a function that draws a square using the canvas API might be myCanvas.square() .

Built-in Objects

We’ve already seen the two main built-in objects included in JavaScript: arrays and functions. JavaScript has a number of other built-inglobalobjects that can be accessed from anywhere in a program. They provide a number of useful properties and methods that we’ll cover in this section.

JSON

JavaScript Object Notation, or JSON, was invented by Douglas Crockford in 2001. It is an extremely popular lightweight data-storage format that is used by a large number of services for data serialization and configuration. It is often used to exchange information between web services, and is employed by sites such as Twitter, Facebook and Trello to share information. The beauty of JSON is that it manages to hit the sweet spot between being both human- and machine-readable.

JSON is a string representation of the object literal notation that we have just seen. There are, however, a couple of key differences:

  1. Property names must be double-quoted
  2. Permitted values are double-quoted strings, numbers, true, false, null, arrays and objects
  3. Functions are not permitted values

A JSON string representation the Caped Crusader is shown below:

const batman = '{"name": "Batman","real name": "Bruce Wayne","height": 74, "weight": 210, "hero": true, "villain": false, "allies": ["Robin","Batgirl","Superman"]}'

JSON is becoming increasingly popular as a data storage format, and many programming languages now have libraries dedicated to parsing and generating it. Since ECMAScript 5, there has been a global JSON object that has methods to allow this to be done in JavaScript.

The parse() method takes a string of data in JSON format and returns a JavaScript object:

JSON.parse(batman);<< { name: 'Batman','real name': 'Bruce Wayne',height: 74,weight: 210,hero: true,villain: false,allies: [ 'Robin', 'Batgirl', 'Superman' ] }

The stringify() method does the opposite, taking a JavaScript object and returning a string of JSON data, as can be seen in the example:

const wonderWoman = {    name: 'Wonder Woman',    'real name': 'Diana Prince',    height: 72,    weight: 165,    hero: true,    villain: false,    allies: ['Wonder Girl','Donna Troy','Superman'],    lasso: function(){        console.log('You will tell the truth!');    }}JSON.stringify(wonderWoman);<< '{"name":"Wonder Woman","real name":"Diana Prince","height":72,"weight":165,"hero":true,"villain":false,"allies":["Wonder Girl","Donna Troy","Superman"]}'

Note that the lasso method is simply ignored by the stringify() method.

You can also add a space argument that will add new lines between each key-value pair, which is useful when displaying the results in a browser:

JSON.stringify(wonderWoman, null, " ");<<  '{\n "name": "Wonder Woman",\n "real name": "Diana Prince",\n "height": 72,\n "weight": 165,\n "hero": true,\n "villain": false,\n "allies": [\n  "Wonder Girl",\n  "Donna Troy",\n  "Superman"\n ]\n}'

These methods are particularly useful when it comes to sending data to, and receiving data from, a web server using Ajax requests (see Chapter 13) – or when using localStorage to store data on a user’s machine (see Chapter 14). JSON data is easy to exchange between different services, as most languages and protocols are able to interpret data as strings of text – and they only need to be stored as a basic text file.

The Math Object

The Math object is a built-in object that has several properties representing mathematical constants, as well as methods that carry out a number of common mathematical operations.

All the properties and methods of the Math object are immutable and unable to be changed.

Mathematical Constants

The Math object has eight properties that represent a mix of commonly used math constants. Note that they are all named in capital letters, as is the convention for constant values:

Math.PI // The ratio of the circumference and diameter of a circle<< 3.141592653589793Math.SQRT2 // The square root of 2<< 1.4142135623730951Math.SQRT1_2 // The reciprocal of the square root of 2<< 0.7071067811865476Math.E // Euler's constant<< 2.718281828459045Math.LN2 // The natural logarithm of 2<< 0.6931471805599453Math.LN10 // The natural logarithm of 10<< 2.302585092994046Math.LOG2E // Log base 2 of Euler's constant<< 1.4426950408889634Math.LOG10E // Log base 10 of Euler's constant<< 0.4342944819032518

Mathematical Methods

The Math object also has several methods to carry out a variety of useful mathematical operations.

Absolute Values

The Math.abs() method returns the absolute value of a number. So if the number is positive, it will remain the same, and if it’s negative, it will become positive:

Math.abs(3);<< 3Math.abs(-4.6);<< 4.6

Rounding Methods

The Math.ceil() method will round a numberupto the next integer, or remain the same if it is already an integer:

Math.ceil(4.2);<< 5Math.ceil(8);<< 8Math.ceil(-4.2);<< -4

The Math.floor() method will round a numberdownto the next integer, or remain the same if it is already an integer:

Math.floor(4.2);<< 4Math.floor(8);<< 8Math.floor(-4.2);<< -5

The Math.round() method will round a number to thenearestinteger:

Math.round(4.5);<< 5Math.round(4.499);<< 4Math.round(-4.2);<< -4

ES6 also introduced the Math.trunc() method that returns the integer-part of a number – that is, it gets truncated at the decimal point:

Math.trunc(4.9);<< 4Math.trunc(-4.2);<< -4

Powers and Roots

The Math.exp() method will raise a number to the power of Euler’s constant:

Math.exp(1); // This is Euler's constant<< 2.718281828459045Math.exp(0); // Any number to the power of 0 is 1<< 1Math.exp(-3);<< 0.049787068367863944

The Math.pow() method will raise any number (the first argument) to the power of another number (the second argument):

Math.pow(3, 2); // 3 squared<< 9Math.pow(4.5, 0); // Any number to the power of 0 is 1<< 1Math.pow(27, 1/3); // A nice way to do cube roots<< 3

The Math.sqrt() method returns the positive square root of a number:

Math.sqrt(121);<< 11Math.sqrt(2); // same as Math.SQRT2<< 1.4142135623730951Math.sqrt(-1); // imaginary numbers aren't supported!<< NaN

The Math.cbrt() method was introduced in ES6, which returns the cube root of numbers:

Math.cbrt(8);<< 2Math.cbrt(-1000);<< -10

The Math.hypot() method was also introduced in ES6. It returns the square root of the sum of the squares of all its arguments. This can be used to calculate the hypotenuse of a right-angled triangle:

Math.hypot(3,4); // returns the square root of 3 squared + 4 squared<< 5Math.hypot(2,3,6); // more than 2 arguments can be used<< 7

Logarithmic Methods

The Math.log() method returns the natural logarithm of a number:

Math.log(Math.E); // Natural logs have a base of Euler's constant<< 1Math.log(1); // log of 1 is zero<< 0Math.log(0); // You can't take the log of zero<< -InfinityMath.log(-2); // You can't take logs of negative numbers<< NaN

Logarithms in base 2 and 10 were added in ES6:

Math.log2(8); // 8 is 2 to the power of 3<< 3Math.log10(1000000); // 1 million is 10 to the power 6<< 6

Maximum & Minimum Methods

The Math.max() method returns the maximum number from its arguments:

Math.max(1,2,3);<< 3Math.max(Math.PI,Math.SQRT2, Math.E);<< 3.141592653589793

And the Math.min() method unsurprisingly returns the minimum number from the given arguments:

Math.min(1,2,3);<< 1Math.min(Math.PI,Math.SQRT2, Math.E);<< 1.4142135623730951

Trigonometric Functions

The Math object also has the standard trigonometric functions, which are very useful when working with geometrical objects. All angles are measured in radians for these functions.

Radians

Radiansare a standard unit of angular measurement, equal to the angle of the circle’s center corresponding to the arc that subtends it.

Rounding Errors

Be careful if you require exact answers to these calculations, as rounding errors in the background mean the returned value is often slightly inaccurate.

A number of these errors are highlighted in the examples below.

This is to be expected when dealing with floating-point decimal numbers. Computers have lots of trouble dealing with decimal fractions (as they work in binary), and the answers can vary from one platform to another.

Another problem is that the value of π using Math.PI is only given correct to 16 significant figures, which will affect the overall accuracy.

These issues are also implementation dependent, which means they rely on the JavaScript engine and operating system they are running on rather than the language itself. So you may get slightly different answers using a different web browser on the same OS or using the same web browser on a different OS!

These rounding errors shouldn’t be a big deal for most web applications. Whenever you perform any calculations, make sure your program doesn’t rely on exact answers, and has some degree of tolerance instead.

If you find you need more precision, you could consider usingdecimal.js library.

The Math.sin() returns the sine of an angle:

Math.sin(Math.PI/6); // this calculation contains rounding errors, it should be 0.5<< 0.49999999999999994

The Math.cos() returns the cosine of an angle:

Math.cos(Math.PI/6);<< 0.8660254037844387

The Math.tan() returns the tangent of an angle:

Math.tan(Math.PI/4); // another rounding error, this should be 1<< 0.9999999999999999Math.tan(Math.PI/2); // this should be NaN or Infinity<< 16331778728383844

The Math.asin() returns the arcsine of a number. The result is an angle:

Math.asin(1);<< 1.5707963267948966

The Math.acos() returns the arccosine of a number. The result is an angle:

Math.acos(0.5);<< 1.0471975511965976

The Math.atan() returns the arctangent of a number. The result is an angle:

Math.atan(Math.sqrt(3)); // Same as Math.PI/3<< 1.0471975511965976

Methods for thehyperbolic functionssinh()cosh() and tanh() were also added in ES6, as well as their inverses:

Math.sinh(1);<< 1.1752011936438014Math.asinh(1.1752011936438014);<< 1Math.cosh(0);<< 1Math.acosh(1);<< 0Math.tanh(10);<< 0.9999999958776927Math.atanh(0.9999999958776927); // rounding error here<< 9.999999995520374

Random Numbers

The Math.random() method is used to create random numbers, which can be very useful when writing programs. Calling the method will generate a number between 0 (inclusive) and 1 (exclusive), like so:

Math.random();<< 0.7881970851344265

To generate a random number between 0 and another number, we can multiply the value by that number. The following code generates a random number between 0 and 6:

6 * Math.random();<< 4.580981240354013

If we want to generate a random integer, we can use the Math.floor() method that we saw earlier to remove the decimal part of the return value. The following code generates a random integer between 0 and 5 (it will never be 6, because it always rounds down):

Math.floor(6 * Math.random());<< 4

It’s a useful exercise to try and write a function that will generate a random number between two values.

The Date Object

Date objects contain information about dates and times. Each object represents a single moment in time.

Constructor Function

A constructor function is used to create a new date object using the new operator:

const today = new Date();

The variable today now points to a Date object. To see what the date is, we use the toString() method that all objects have:

today.toString();<< 'Tue Feb 14 2017 16:35:18 GMT+0000 (GMT)'

If an argument is not supplied, the date will default to the current date and time. It’s possible to create Date objects for any date by supplying it as an argument to the constructor function. This can be written as a string in a variety of forms:

const christmas = new Date('2017 12 25');christmas.toString();<< 'Mon Dec 25 2017 00:00:00 GMT+0000 (GMT)'const chanukah = new Date('12 December 2017');// First day of Chanukahchanukah.toString();<< 'Tue Dec 12 2017 00:00:00 GMT+0000 (GMT)'const eid = new Date('Sunday, June 25, 2017');// Eid-al-Fitreid.toString();<< 'Sun Jun 25 2017 00:00:00 GMT+0100 (BST)'

As you can see, the string passed to the Date constructor can be in a variety of formats. However, in order to be more consistent, it’s better to provide each bit of information about the date as a separate argument. The parameters that can be provided are as follows:

new Date(year,month,day,hour,minutes,seconds,milliseconds)

Here is an example:

const solstice = new Date(2017, 5, 21);// Summer Solsticesolstice.toString();<< 'Wed Jun 21 2017 00:00:00 GMT+0100 (BST)'

Remember that computer programs start counting at zero, so January is 0, February is 1, and so on up to December, which is 11.

An alternative is to use a timestamp, which is a single integer argument that represents the number of milliseconds since the Epoch (1st January 1970):

const diwali = new Date(1508367600000);diwali.toString();<< 'Thu Oct 19 2017 00:00:00 GMT+0100 (BST)'

The Epoch

The Epoch is 1st January 1970. This is an arbitrary date that is used in programming as a reference point in time from which to measure dates. This allows dates to be expressed as an integer that represents the number of seconds since the Epoch. It results in a very large number and there is a potentialproblem looming in 2038when the number of seconds since the Epoch will be greater than 2,147,483,647, which is the maximum value that many computers can deal with as a signed 32-bit integer. Fortunately, this problem will not affect JavaScript dates because it uses floating-point numbers rather than integers, so it can handle bigger values.

Getter Methods

The properties of date objects are unable to be viewed or changed directly. Instead, they have a number of methods known asgettermethods, which return information about the date object, such as the month and year.

Once you’ve created a date object it will have access to all the getter methods. There are two versions of most methods – one that returns the information in local time, and the other that uses Coordinated Universal Time (UTC). The getTime()getTimezoneOffset() and getYear() methods don’t have UTC equivalents.

UTC

UTC is the primary time standard by which the world regulates clocks. It was formalized in 1960 and is much the same as Greenwich Mean Time (GMT). The main difference is that UTC is a standard that is defined by the scientific community, unlike GMT.

The getDay() and getUTCDay() methods are used to find the day of the week that the date object falls on. It returns a number, starting at 0 for Sunday, up to 6 for Saturday:

diwali.getDay(); // it's on a Thursday<< 4

The getDate() and getUTCDate() methods return the day of the month for the date object (note that these values start counting from 1, not 0, so they return the actual day of the month):

diwali.getDate(); // it's on the 19th<< 19

The getMonth() and getUTCMonth() methods can be used to find the month of the date object. It returns an integer, but remember to count from 0; so January is 0, February is 1, and so on up to December being 11:

diwali.getMonth(); // it's in October<< 9

The getFullYear() and getUTCFullYear() methods return the year of the date object. There is also a getYear() method, but it isn’t Y2K compliant, so shouldn’t be used:

diwali.getYear(); // broken for years after 2000<< 117diwali.getFullYear(); // use this instead<< 2017

There are also getHours()getUTCHours()getMinutes()getUTCMinutes()getSeconds()getUTCSecondsgetMilliseconds() , and getUTCMilliseconds() methods that will return the hours, minutes, seconds and milliseconds since midnight.

The getTime() method returns a timestamp representing the number of milliseconds since the Epoch:

diwali.getTime();<< 1508367600000

This can be useful for incrementing dates by a set amount of time. For example, a day can be represented by 1000 * 60 * 60 * 24 milliseconds:

const christmasEve = new Date(christmas.getTime() - 1000 * 60 * 60 * 24) // one day before ChristmaschristmasEve.toString();<< Fri Dec 26 2014 00:00:00 GMT+0000 (GMT)"

The getTimezoneOffset() method returns the difference, in minutes, between the local time on the computer and UTC. For example, my timezone is currently the same as UTC, so it returns 0:

new Date().getTimezoneOffset();<< 0

Setter Methods

Most of the getter methods covered in the previous section have equivalentsettermethods. These are methods that can be used to change the value of the date held in a Date object. Each of the methods takes an argument representing the value to which you update the date. The methods return the timestamp of the updated date object.

As an example, we can change the value of the date stored in the diwali variable so that it contains the date of Diwali in 2018, which is on Wednesday, November 7, 2018:

diwali.setDate(7);<< 1507330800000diwali.setMonth(10); // November is month 10<< 1510012800000diwali.setFullYear(2018);<< 1541548800000

Note that the values returned by these functions is the timestamp representing the number of milliseconds since the Epoch. To see the actual date, we need to use the toString() method:

diwali.toString();<< 'Wed Nov 07 2018 00:00:00 GMT+0000 (GMT)'

There are also setHours()setUTCHours()setMinutes()setUTCMinutes()setSeconds()setUTCSecondssetMilliseconds() and setUTCMilliseconds() methods that can be used to edit the time portion of a Date object.

Alternatively, if you know the date as a timestamp, you can use the setTime() method:

diwali.setTime(1447200000000);<< 1541548800000

Tricky Timezones

Working with dates and timezones can be tricky. Themoment.js librarygives you a large number of methods that make it easier to work with dates, as well as support for multiple locales.

The RegExp Object

A regular expression (or RegExp, for short) is a pattern that can be used to search strings for matches to the pattern. A common use is ‘find and replace’ type operations. For example, say you were looking for any word ending in ‘ing’, you could use the regular expression /[a-zA-Z]+ing$/ .

If that example looks a bit confusing, don’t worry, it will become clear as we move through this section. Regular expressions can look a little strange; in fact, they’re something of a dark art that could easily fill a whole book! They are certainly useful when manipulating text strings, though, so we’ll introduce some of the basics here and recommend that you carry out further reading once you’ve finished this book.

Here are a couple of resources for the curious:

Creating Regular Expressions

There are two ways to create a regular expression. The first, and preferred way, is to use the literal notation of writing the regular expression between forward slashes that we’ve already seen:

const pattern = /[a-zA-Z]+ing$/;

Alternatively, you can create a new instance of the RegExp object using the new operator and a constructor function:

const pattern = new RegExp('[a-zA-Z]+ing');

Notice that the backslash character needs to be used twice in the last example.

Using literal regular expressions takes less typing, but there are advantages to using the constructor function as it lets you create regular expressions using strings, which can be useful when the regular expression is provided from user input; in a form, for example. Constructors also have the advantage of letting you create a regular expression using a variable:

const language = 'JavaScript';const pattern = new RegExp(language);

RegExp Methods

Once you’ve created a regular expression object, you can use the test() method to see if a string (passed to the method as a parameter) matches the regular expression pattern. It returns true if the pattern is in the string, and false if it isn’t.

We can see an example of the test() method used below, using the same pattern we created earlier that tests if a word ends in ‘ing’:

pattern.test('joke');<< falsepattern.test('joking');<< truepattern.test('jokingly');<< false

The exec() method works in the same way as the test() method, but instead of returning true or false , it returns an array containing the first match found, or null if there aren’t any matches:

pattern.exec('joke'); << nullpattern.exec('joking');<< [ 'joking', index: 0, input: 'joking' ]

Basic Regular Expressions

At the most basic level, a regular expression will just be a string of characters, so the following will match the string ‘JavaScript’:

const pattern = /JavaScript/;<< /JavaScript/

Character Groups

Groups of characters can be placed together inside square brackets. This character group represents anyoneof the characters inside the brackets. For example, the following regular expression matches any vowel:

const vowels = /[aeiou]/<< /[aeiou]/

A sequence of characters can also be represented by placing a dash [ - ] between the first and last characters; for example, all the uppercase letters can be represented as:

/[A-Z]/

The digits 0-9 can be represented as:

/[0-9]/

If a \^ character is placed at the start of the sequence of characters with the brackets, it negates the sequence, so the following regular expression represents any character that isnota capital letter:

/[^A-Z]/

These groups can be combined with letters to make a more complex pattern. For example, the following regular expression represents the letter J (lowercase or capital) followed by a vowel, followed by a lowercase v, followed by a vowel:

pattern = /[Jj][aeiou]v[aeiou]/;<< /[Jj][aeiou]v[aeiou]/pattern.test('JavaScript');<< truepattern.test('jive');<< truepattern.test('hello');<< false

Regular Expression Properties

Regular expressions are objects, and have the following properties:

  • The global property makes the pattern return all matches. By default, the pattern only looks for the first occurrence of a match.
  • The ignoreCase property makes the pattern case-insensitive. By default, they are case sensitive.
  • The multiline property makes the pattern multiline. By default, a pattern will stop at the end of a line.

The following flags can be placed after a regular expression literal to change the default properties:

  •  g sets the global property to true
  •  i sets the ignoreCase property to true
  •  m sets the multiline property to true

For example, the following regular expression will match ‘JavaScript’ or ‘javascript’ because the ignoreCase property is set to true :

pattern = /java/i<< /java/ipattern.test('JavaScript');<< true

These properties can be checked using the dot notation, but cannot be updated once the regular expression has been created, as can be seen in the following example:

pattern = /java/i<< /java/ipattern.ignoreCase // checking it is true<< truepattern.ignoreCase = false // this won't work<< falsepattern.ignoreCase // has it changed? Nope!<< true

The only way to change the ignoreCase property to false is to redefine the regular expression:

pattern = /java/<< /java/

Special Characters

In a regular expression, there are a number of characters that have a special meaning, commonly known as metacharacters:

  •  . matches any character, except line breaks
  •  \w matches any word character, and is equivalent to [A-Za-z0-9_]
  •  \W matches any non-word character, and is equivalent to [\^A-Za-z0-9_]
  •  \d matches any digit character, and is equivalent to [0-9]
  •  \D matches any non-digit character, and is equivalent to [^0-9]
  •  \s matches any whitespace character, and is equivalent to [ \t\r\n\f]
  •  \S matches any non-whitespace character, and is equivalent to [^ \t\r\n\f]

Modifiers

Modifiers can be placed after a token to deal with multiple occurrences of that token:

  •  ? makes the preceding token in the regular expression optional
  •  * matches one or more occurrences of the preceding token
  •  + matches one or more occurrences of the preceding token
  •  {n} matchesnoccurrences of the preceding token
  •  {n,} matches at leastnoccurrences of the pattern
  •  {,m} matches at mostmoccurrences of the preceding token
  •  {n,m} matches at leastnand at mostmoccurrences of the preceding token
  •  ^ marks the position immediately before the first character in the string
  •  $ marks the position immediately after the last character in the string

Any special characters or modifiers can be escaped using a backslash. So if you wanted to match a question mark, ? , you would need to use the regular expression /\?/ .

For example, the following regular expression will match anything that starts with J followed by one or more vowels, then any letters or numbers ending in ing :

pattern = /^J[aeiou]+\w+ing$/<< /J[aeiou]+\w+ing/

As we can see, it now matches the words ‘Joking’ and ‘Jeering’:

pattern.test('Joking');<< truepattern.test('Jeering');<< true

Greedy and Lazy Modifiers

All the modifiers above aregreedy, which means they will match the longest possible string. They can be made intolazymodifiers that match the shortest possible string by adding an extra ‘?’ after the modifier.

For example, consider the string ‘abracadabra’:

const word = 'abracadabra';

The greedy pattern /a.+a/ will return the whole string because it is the longest string that matches the pattern of ‘a’, followed by numerous characters and finishing with an ‘a’:

const greedyPattern = /a.+a/;greedyPattern.exec(word);<< [ 'abracadabra', index: 0, input: 'abracadabra' ]

The lazy pattern /a.+?a/ changes the + modifier to +? . This will only return the string ‘abra’ as this is the shortest string that matches the pattern ‘a’ followed by some characters and ending in an ‘a’.

const lazyPattern = /a.+?a/;lazyPattern.exec(word);<< [ 'abra', index: 0, input: 'abracadabra' ]

A Practical Example

If we were looking for PDF files and had a list of filenames, this regular expression could be used to find them (assuming they have a .pdf extension, of course):

const pdf = /.*\.pdf$/;

This looks for zero or more occurrences of any character, followed by an escaped period, followed by the letters ” pdf ” that must come at the end of the string:

pdf.test('chapter5.pdf');<< truepdf.test('report.doc');<< false

String Methods

There are a number of string methods that accept regular expressions as a parameter.

The split() method we saw in Chapter 2 can also accept a regular expression that’s used to split a string into the separate elements of an array. The following example uses a regular expression to split a string every time there are one or more occurrences of a whitespace character:

'Hello World!'.split(/\s+/) //<< ['Hello', 'World!']

The match() method returns an array of all the matches. By default, only the first is returned:

'JavaScript'.match(/[aeiou]/); // return the first vowel<< ['a']

We can use the g flag to returnallthe matches:

'JavaScript'.match(/[aeiou]/g); // return an array of all the vowels<< ['a', 'a', 'i']

The search() method returns the position of the first match:

"I'm learning JavaScript".search(/java/i);<< 13

It returns -1 if there is no match:

"I'm learning JavaScript".search(/ruby/i);<< -1

The replace() method replaces any matches with another string. The following example will replace all vowels with a ‘*’ character:

'JavaScript'.replace(/[aeiou]/ig,'*');<< 'J*v*Scr*pt'

Matched Groups

Sub-patterns can be created inside a regular expression by placing them inside parentheses. These are known ascapturing groups. Any matches to these will then be stored in an array of matches.

Each capturing group is numbered according to the position it appears in the pattern. For example, the first capturing group will be numbered 1, and the second 2, etc. The matches will also be stored in special predefined variables $1$2 etc.

To demonstrate this, here’s an example that searches a string and replaces any HTML anchor tags withMarkdownnotation:

const link = "<a href='https://www.sitepoint.com' title='Oh Yeah!'>Awesome Web Resources</a>"const mdLink = link.replace(/<a href='(.*?)'.*?>(.*?)<\/a>/g, "[$2]($1)");mdLink<< [Awesome Web Resources](https://www.sitepoint.com)

The example has two capturing groups – the first captures any text inside the href attribute and stores it in the variable $1 and the second captures the text inside the anchor tags and stores it in the variable $2 . These matches can then be used to create the link using Markdown.

Quiz Ninja Project

Now it’s time to take another look at our Quiz Ninja project. We’re going to store our questions as objects inside an array. Open up main.js and enter the following at the top of the file:

const quiz = [    { name: "Superman",realName: "Clark Kent" },    { name: "Wonder Woman",realName: "Diana Prince" },    { name: "Batman",realName: "Bruce Wayne" },];

Each element in the array contains information about the superheroes used in our quiz. These objects replace the nested arrays we used in the previous chapters, and have properties of name and realName that will be used to form the questions and answers.

Now we’re going to namespace the functions we created in the last chapter. We do this by placing them inside an object called game that will be the namespace. This means that any references to the functions need to be replaced with game.function() outside the object or this.function() inside the object.

Add the following code below the array of questions:

const game = {    start(quiz){        this.questions = [...quiz];        this.score = 0;        // main game loop        for(const question of this.questions){        this.question = question;        this.ask();        }        // end of main game loop        this.gameOver();    },    ask(){        const question = `What is ${this.question.name}'s real name?`;        const response =  prompt(question);        this.check(response);    },    check(response){        const answer = this.question.realName;        if(response === answer){        alert('Correct!');        this.score++;        } else {        alert(`Wrong! The correct answer was ${answer}`);        }    },    gameOver(){        alert(`Game Over, you scored ${this.score} point${this.score !== 1 ? 's' : ''}`);    }}

After this, we have to edit the function that starts the game, so it includes the namespace:

game.start(quiz);

Save these changes then have a go at playing the game again. Once again, we haven’t actually added any functionality, but we have made our code more organized by placing all of the functions inside an object. This will make it easier to expand on the functionality in later chapters.

Quiz Ninja

You can see a live example onCodePen.

Chapter Summary

  • Objects are a collection of key-value pairs placed inside curly braces {}.
  • Objects have properties that can be any JavaScript value. If it’s a function, it’s known as amethod.
  • An object’s properties and methods can be accessed using either dot notation or square bracket notation.
  • Objects are mutable, which means their properties and methods can be changed or removed.
  • Objects can be used as parameters to functions, which allows arguments to be entered in any order, or omitted.
  • Nested objects can be created by placing objects inside objects.
  • JSON is a portable data format that uses JavaScript object literals to exchange information.
  • The Math object gives access to a number of mathematical constants.
  • The Math object can be used to perform mathematical calculations.
  • The Date object can be used to create date objects.
  • Once you’ve created a Date object, you can use the getter methods to access information about that date.
  • Once you’ve created a Date object, setter methods can be used to change information about that date.
  • The Regex object can be used to create regular expressions.

Now we’ve reached the end of the first part of the book, you should have a good grasp of the JavaScript programming language basics. But JavaScript was originally designed to be used in the browser, so in the next chapter we’ll look at how to use JavaScript to interact with web pages.

Pages: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16