Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
# Functions and Objects
## 1 Functions
In JavaScript, as in most other languages, code can be divided in to modular blocks called functions. Once defined, these can be called from other code. Data can be passed in the form of parameters and functions can return data back to the calling code.
Open the `maths.js` file. Notice that this contains several functions. Each is called directly under its definition.
### 1.1 Function Syntax
Lets start with a simple example.
```javascript
function largestNumber(a, b) {
if (a > b) return a
if (b > a) return b
return null
}
const biggest = largestNumber(5, 8)
```
1. The function is declared using the `function` keyword and the function is given a name which must be a valid variable name.
a. If the name comprises more than one word these should be written using camel casing as shown above.
2. The function above takes two parameters, `a` and `b`.
- These are variables with local scope (they can't ba accessed outside the function)
- When the function is called, you need to pass two **values** which get assigned to the two parameters.
- If you pass too many values the extra ones get _ignored_.
- If you don't pass enough values the remainder are assigned a value of `null`. `Null` is an assignment value (means a value of no value).
3. The function returns a value.
a. If the numbers are not the same it returns the largest.
b. If they are the same it returns `null`.
### 1.2 The Spread Operator
If the data you want to pass to a function is stored in an `Array` (this is quite common), you could extract each value and assign to the function like this:
```javascript
const nums = [5, 8]
const biggest2 = largestNumber(nums[0], nums[1])
```
Because this is such a common task, there is a shortcut called the **spread operator**. Using this, the same task can be expressed like this.
```javascript
const nums = [5, 8]
const biggest2 = largestNumber(...nums)
```
Notice the syntax of the _spread operator_.
### 1.3 The Arguments Object
When a function is declared it has a **signature** which defines the number of parameters it is expecting, for example in the `largestNumber()` function, the signature defines two arguments.
```javascript
function largestNumber(a, b) { // this is the function signature.
// function body
}
```
What happens if you try to call this with the _wrong_ number of arguments?
- If you supply too few arguments the remaining parameters are assigned a data type and value of `null`.
- If you supply too many arguments, the remaining ones are not assigned to the parameters, so where are they?
Every JavaScript function has an object called `Arguments` which contains all the parameters passed to that function, even ones no assigned to the formal parameters. This provides a mechanism to access arguments that don't get assigned to parameters. Lets take a look at the `add()` function.
```javascript
function add() {
let total = 0
console.log(arguments)
console.log(arguments['1'])
for(const arg of arguments) {
total += arg
}
return total
}
```
As you can see, the function signature defines no parameters, but when we call it we pass 4 arguments. What happens to these and how can we access them?
```javascript
const addNums = add(1, 2, 3, 4)
```
Inside the function we can access the `arguments` object. The `add()` function shows three ways to do this (we will be covering objects in detail in the next chapter):
- display all the arguments (see below).
- access individual arguments by referencing a key.
- Use a `for...of` loop to iterate through the values.
### 1.4 The Rest Parameter
Whilst the `arguments` object provides a mechanism for accessing the function arguments, it returns an Object (the keys are `Strings`). It would be better if
- the arguments could be accessed in an `Array`.
- it ignored arguments already assigned to parameters.
ECMA6 introduced a special parameter called a _rest parameter_ which captures all the arguments that have not been assigned to parameters and stores them in an array. Look at the `add2()` function.
```javascript
function add2(...values) {
let total = 0
console.log(values)
console.log(values[1])
for (let i=0; i<values.length; i++) {
total += values[i]
}
return total
}
```
The `...values` parameter has a `...` prefix which defines it as a _rest parameter_. In the body of the function it can be seen that this is an `Array` and so each argument has a numerical index. This is the preferred way to handle arguments that are not assigned to parameters.
### 1.5 Default Parameters
As explained above, if you don't supply enough arguments for the parameters in the function signature, all the parameters without arguments are assigned a value of `null`. This means you have to add code within the function to check that there is a value assigned to the parameters before you can safely use them. ECMA6 has introduced **default parameters**. These allow you to assign a default value to a parameter if one is not supplied by when the function is called. Lets examine the `divide()` function.
```javascript
function divide(dividend, divisor=1) {
const quotient = dividend / divisor
return quotient
}
```
Notice that the divisor has been assigned a value in the function signature. If this parameter is not assigned an argument, it defaults to this value.
### 1.6 Function Expressions
Functions are a data type in JavaScript (they are objects but more on that in the next chapter). As such they can be stored in variables for later execution. Prior to ECMA6 they were declared using the `function` keyword like this:
```javascript
const remainder = function(dividend, divisor) {
const quotient = Math.floor(dividend / divisor)
return dividend - quotient
}
```
To execute the function you simply reference the variable and append `()`.
```javascript
const rem = remainder(8, 5)
```
ECMA6 introduced a better way to handle function expressions, called an **arrow function expression**. This has a much shorter (and cleaner) syntax. Here is the same function expression written using this new syntax, make a careful note of the differences.
```javascript
const remainder2 = (dividend, divisor) => {
const quotient = Math.floor(dividend / divisor)
return dividend - quotient
}
```
The _arrow function expression_ has a number of important features:
1. It does not have its own function scope which means it does not bind its own `this` object (made clearer later).
2. In a concise body (one line) it has an implicit return and you don't need to use block braces. This results in very concise code, see the example below).
3. If there is only a single parameter the parameter brackets can be omitted.
Here is an example that should make points 2 and 3 clearer.
```javascript
const sqr = num => num * num
```
### 1.7 Test Your Knowledge
Start by running the `maths.js` script and map the output it generates against the `console.log` statements in the script.
1. Create a new function called `multiply()` that takes two parameters, `a` and `b` and returns the _product_ of the two.
- what happens if you call it with only a single parameter?
2. Modify the function so it uses a default parameter to multiply by 1 if the second parameter is missing.
- What happens if you don't supply _any_ parameters?
- Add a second default parameter to prevent this.
3. Write an _arrow function expression_ stored in a constant called `squareRoot` which calculates and returns the square root of the supplied number. You will need to use the `sqrt()` method which is part of the `Math` object.
### 1.8 Test Your Knowledge (Again)
Open the `contact.js` script, implement the `validateEmail()` function and thoroughly test it, you should avoid using regular expressions at this stage:
1. Check that the string is at least 5 character long
2. Check that there is a `@` character and that it is not at the start of the string (HINT: use the [indexOf](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf) String prototype method.
3. Check that there is a period (.) character after the `@` character but before the end of the string.
### 1.9 Callbacks
Since JavaScript supports _first class functions_, we can use a function in any place where we could use a literal, object or variable. Open the `currency.js` script and look at line 17. As you can see the `request` object has a key called `get` that stores a function (we have already covered this). This takes two parameters:
1. A string representing the url to be accessed.
2. A function that will be called once the data has been retrived from the url. This was defined earlier in the script and takes 3 parameters.
```javascript
request.get(url, printRates)
```
This is a common construct used in JavaScript/NodeJS. The second function parameter is known as a **callback**.
NodeJS is a single-threaded event loop that processes queued events. This means that if you were to execute a long-running task within a single thread then the process would block. To solve this problem, NodeJS relies on callbacks, which are functions that run after a long-running process has finished. Instead of waiting for the task to finish, the event loop moves on to the next piece of code. When the long-running task has finished, the callback is added to the event loop and run.
Because callbacks are such a fundamental part of NodeJS you need to spend time to make sure you fully understand how they work.
Try running the program and note the output (remember you will need to install the `request` package.
1. The currency code we want to convert to is stored in a constant.
2. We create a _function expression_ called `printRates()` which has three parameters and prints out data from the response:
1. An error, this will be `null` if there is no error.
2. An object containing all the data from the HTTP response.
3. The string returned in the response body.
3. Next we use a [template literal](https://goo.gl/3vznuR) to create the URL we will be calling.
4. Finally we call the function stored in the `get` key in the `request` object and pass the string and function literal we created earlier.
#### 1.9.1 Using Anonymous Functions in Callbacks
Although this code works, you will rarely see callbacks written in this manner. Creating a function literal is a bit clunky and we can clean up the code by simply passing an anonymous function.
```javascript
request.get( url, (err, res, body) => {
// callback code goes here.
})
```
Take a few moments to make sure you fully understand the syntax, you will be seeing a lot of this over the next few weeks.
### 1.9.2 Test Your Understanding
Lets improve the currency exchange tool. You will need to refer to the API [documentation](http://fixer.io) as you work through the tasks.
1. Replace the _function expression_ with an _anonymous function_.
2. Print the entire response body to the terminal window using [json.stringify()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).
3. The **base rate** defaults to the € (EUR) however the API allows you to set a different currency as the base rate:
1. Add a constant `base` to store your preferred currency.
2. Modify the URL using the API documentation to guide you.
4. Use the [Number.prototype.toFixed()](https://goo.gl/DU4hvd) to truncate the number to 2 decimal places.
5. Finally, modify your program so that it throws an error if it doesn't recognise either of the currency codes.
## 2 Objects
Lets start by creating an manipulating objects using **object literals**. Open the `employee.js` file, read through it and see if you can work out what it does. Now run it to see if you were correct.
### 2.1 Creating Object Literals
The simplest way to create new objects is by creating an _object literal_ which is defining an object and storing it in a variable. You should open the `employee.js` file which contains the code.
```javascript
const employee = {
firstName: 'Colin',
'last name': 'Stephen'
}
```
As you can see from the simple example above, the data is stored in name-value pairs, referred to as **Properties**. This example is defining an object with **3** properties.
The _name_ part of each property is a JavaScript string which may be enclosed in single quotes. These quotes are optional if the _property name_ is a valid _JavaScript variable_ but they are required if this is not the case.
In the example above, `firstName` is a valid JavaScript variable but `last name` is not because it contains a space which is not allowed in variable names.
It is also possible to create an empty object (we can add properties later). This is done by assigning empty curly braces.
```javascript
const emptyObject = {}
```
Here are some valid _property names_. Notice that both `age` and `'age'` are valid.
```
age
'first name'
'age'
```
The _property names_ below are **not** valid because they are not a valid JavaScript variable names.
```
first name
firstName!
first-name
```
#### 2.1.1 Test Your Understanding
1. Add a property called `gender` and assign a suitable String value.
2. Add a new property called `date of birth` that stores the year the person was born and assign a suitable value.
### 2.2 Retrieving Object Properties
Whilst it is possible (and useful) to log an entire object to the console, normally we would want to retrieve the values of specific properties.
```javascript
const employee = {
firstName: 'Colin',
'last name': 'Stephen',
'department': 'Computing'
}
console.log(employee)
const firstName = employee.firstName
const lastName = employee['last name']
const grade = employee.grade
```
Passing the object name to `console.log()` will print out the string representation of the object. To retrieve a specific property value there are two options. If the name is a _legal JS variable name_ the dot `.` notation can be used. This is used to log the `firstName` property in the example above.
If the name is not a valid JavaScript variable name we need to turn it into a string by using quotes `''` and enclose it in square braces `[]`. This is used to log the `last name` property.
The `grade` variable will be `undefined` because `employee.grade` does not exist. If you want to avoid this and assign a default value if the property is missing you can use the **OR** operator `||`.
```javascript
const grade = employee.grade || 'A'
```
This will retrieve the value of the grade property if defined and store it in the `const` variable. If this property is missing the `const` variable will contain the string `'A'`.
#### 2.2.1 Getters and Setters
Most object properties are simple values and you can simply assign a value. Sometimes however properties need to be calculated. One solution is to store a function as one of the properties however we would need to call a function to retrieve the value:
```javascript
const employee = {
firstName: 'Colin',
'last name': 'Stephen',
getName: () => `${this.firstName} ${this['last name']}`
}
const name = employee.getName()
```
Whilst this works fine it looks a little clunky. Thankfully in the newer versions of JavaScript you can use a **getter** which makes the code far more intuitive.
```javascript
const employee = {
firstName: 'Colin',
'last name': 'Stephen',
get name() {
return `${this.firstName} ${this['last name']}`
}
}
const name = employee.name
```
In the same manner, some properties when set may need to change other properties. Here is a solution using a stored function.
```javascript
const employee = {
firstName: 'Colin',
'last name': 'Stephen',
setName: function(fullname) {
const words = fullname.toString().split(' ')
this.firstName = words[0] || ''
this['last name'] = words[1] || ''
}
}
employee.setName('Micky Mouse')
```
By using a **setter**, it behaves just like any other property.
```javascript
const employee = {
firstName: 'Colin',
'last name': 'Stephen',
set name(fullname) {
const words = fullname.toString().split(' ')
this.firstName = words[0] || ''
this['last name'] = words[1] || ''
}
}
employee.name = 'Micky Mouse'
```
#### 2.2.2 Test Your Understanding
1. Print the person's details in an easy to understand sentence.
2. Add a getter to return the number of years the employee has been working for the company, you will need to round this down to a whole number. You should make use of one of the static methods of the built-in [Math](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math) object.
### 2.3 Modifying Objects
Once an object has been created it can be modified in several ways.
1. More object values can be added
2. Object names can be deleted
3. The values can be changed for existing names.
Once an object has been created, additional properties cane be added by setting values by assignment.
```javascript
const employee = {
firstName: 'Colin',
'last name': 'Stephen',
'department': 'Computing'
}
employee.grade = 4
employee['currently employed'] = true
employee.department = 'Computer Science'
```
This sets a new value if the name does not already exist. Otherwise, it updates the existing value. Notice that the syntax depends on whether the property name is a valid JavaScript object and matches the syntax used to retrieve a property.
Properties can be removed from an object literal using the `delete` operator. This removes the entire property (name and value).
```javascript
const employee = {
firstName: 'Colin',
'last name': 'Stephen',
'department': 'Computing'
}
delete employee.department
```
### 2.4 Undefined Values
Undefined Objects
If you try to retrieve a value from an object that is undefined, JS throws a TypeError exception:
```javascript
const nonExistentObject.postCode // throws "TypeError"
const addressObject = employee.address // returns undefined
const postCode = employee.address.postCode // throws "TypeError"
```
To see what a `typeError` looks like, try uncommenting the three lines at the end of the `employee.js` file. So how can we avoid this?
The **AND** operator `&&` can be used to guard against this problem.
```javascript
const postCode = employee.address && employee.address.postCode
console.log(postCode) // returns undefined
```
#### 2.4.1 Test Your Understanding
1. Modify the code to handle bad data:
1. Remove the startYear property.
2. Set the startYear property to a String.