addEventListener(‘eventName’, function)
<button class="submit1">Click Me</button>
<script>
const button1 = document.querySelector('.submit1')
button1.addEventListener('click', () => {
alert("Ouch!")
})
</script>
The first argument, ‘click’, is a string event that is supported in the browser. Other events you can add events for include mouseenter, mouseleave, mousemove, and keyup.
select allows you to build dropdown menus. The choices go inside option elements, which are children of the select element.
If we have an array of strings that we want to use for the options, we can use reduce to generate string tags and then set the innerHTML property of the select:
<select class="selectContainer"></select>
<script>
const data = ["Charmander", "Squirtle", "Caterpie"]
const select = document.querySelector('.selectContainer')
select.innerHTML = data.reduce( (acc, e) => {
return acc + `
<option>${e}</option>
`
}, "")
</script>
When data (number, boolean, string, array, objects) is converted into a string, the string is called JSON. JSON stands for JavaScript Object Notation.
Databases also store data as strings, so if you want to store an array of names into the database sometimes you must convert your data to a string first.
cyclic data, which can happen in non-primitive data types, and functions cannot be stringified.
const arr = [-18, "Charizard", true]
const strArr = JSON.stringify(arr)
// strArr is '[-18, "Charizard", true]'
This function takes a string and parses, or interprets it as JavaScript data.
JSON is the string that gets returned by JSON.stringify and the argument you pass into JSON.parse.
JSON.stringify( data ) ——> JSON ——> JSON.parse(JSON) ——> data
LocalStorage lets you store data, like a database in the browser.
localStorage.setItem is a function that takes in 2 strings. The first string tells the browser which title to give the data. The second string is the data to store.
This function takes in only a string, the title of the data to retrieve, and returns the data.
It returns an array of all the elements that match the query.
Objects have a string label. The label is called a key and the content is called the value. Values can be anything, including a function! To create an object you use {}.
const student1 = {
first: "Harry",
last: "Potter",
age: 25
}
const student2 = {
first: "Ron",
last: "Weasley",
age: 24
}
const total = student1.age + student2.age // total is 49
In the example, first is the key, “Harry” is the value, etc.
You can also access the values of an object using the [] notation. To get a value in an object, the code is very similar to getting a value in an array. We pass in a string (key) instead of an index number.
const student = {
first: 'Harry',
last: 'Potter',
age: 25
}
const firstName = student['first'] // firstName has the value 'Harry'
const student2 = student
student2['name'] = 'last'
/*
student2 is the same as student, which is:
{
name: 'last',
first: 'Harry',
last: 'Potter',
age: 25
}
*/
student2[student2.name] = "Weasley"
/*
Notice how student2.name is a variable that contains the string 'last'
Therefore, it evaluates to
student2.last = 'Weasley'
student2 is the same as student, which is:
{
name: 'last',
first: 'Harry',
last: 'Weasley',
age: 25
}
*/
This function takes in an object and returns an array of keys in the object.
This function takes in an object and returns an array of values in the object.
This function takes in an object and returns an array. Each element of the array is an array with 2 elements, a key and its corresponding value in the object.
The hasOwnProperty method allows us to check if a particular property exists ONLY on the object in context and not down the prototype chain of the object.
const myObj = {
name: 'test',
age: 100
}
myObj.hasOwnProperty('name') // true
!!myObj['test'] // true
myObj.hasOwnProperty('age') // true
!!myObj['age'] // true
myObj.hasOwnProperty('height') // false
!!myObj['height'] // false
myObj.hasOwnProperty('toString') // FALSE
!!myObj['toString'] // TRUE
This function deletes the key (and its value) from an object.
- Define your function using function( … params … ) { … code …}
- Assign your function to Object.prototype
- Access object properties using the this keyword
Object.prototype.forEach = function(fun, i = 0, entries = Object.entries(this)) {
if(i === entries.length) return
fun(entries[i][1], entries[i][0])
return this.forEach(fun, i+1)
}
You can also add new properties to this using prototype functions.
Object.prototype.eat = function(value) {
const num = this.data || 0
if (value < num) {
return
}
this.data = value
}
const a = { name: "iron" }
a.eat(5)
// a is: { name: "iron", data: 5 }
When you construct a new Object with the new keyword.
function Person(name, age) {
this.name = name
this.age = age
}
const me = new Person('Joe', 24)
In reality, an array is actually an object, with special status (you can use numbers as keys, etc). You can add keys and values to arrays!
There are two ways to write a function:
- `(…) => {…}` (the “fat arrow” method): Taught in JS0
- `function(…) {…}` First introduced in this chapter for writing prototype functions
What is the difference between the two function declarations? The difference has to do with the this keyword. The Fat Arrow is a lighter way to write functions because unlike the old way of writing function, fat arrow functions do not create a this variable.
You have functions that you want to use in other files. You can simply give the exports key of the module object a value. Whatever value you set will be exported. module.exports is a keyword provided by nodeJS. Any file that exports something can be called a library. Import use `require` to use the exports from other files.
// myObj takes the value of module.exports, which is an object
const myObj = require('./helper1.js')
A simple library that does not require any download is fs, a library that gives you functions to interact with the files and folders on your computer.
const fs = require('fs')
fs.readdir('./', (err, files) => {
console.log(files) // files should be an array
})
fs.readdir takes in 2 arguments, a string and a function. fs.writeFile takes in 3 arguments:
- string (filename)
- string (file content)
- function (to run when the function has finished creating the file; in case you want to check for errors or inform the user that the write succeeded)
fs.writeFile('./today.txt', 'today is a beautiful day', () => {})
An API (application programming interface) is an interface that other engineers set up for you to interact with. There are many libraries we can use to send a request, and the request library is one of them. You might have noticed it in the above examples. You can use this library to send requests to APIs and even to retrieve web pages like your browser does.
npm install request
A typical URL looks like
https://macys.com/shoes?size=4&brand=allbirds&type=outdoors
- **Protocol**: Specifies **how** data is sent over the Internet: https
- **Hostname**: Helps identify **where** to send a request to: macys.com
- **Path**: Helps the server determine **what** to look: shoes
- **Query Parameters**: Helps the server determine **what** to do with the request. Query parameters should be used for data. `&` separates the different pieces of data: size=4&brand=allbirds&type=outdoors
request(“<API ADDRESS HERE>”, (err, res, data) => { console.log(data) })
If you are not careful when using the request library, you may end up in callback hell - complex nested `request` calls. To avoid making mistakes like the above, functions can return an object called a promise. A promise represents the eventual result of an asynchronous action (e.g. making a network request, writing files to the filesystem). The two most commonly used promise methods are:
- `then`
- `then` expects a function argument, which will be called when the promise is done.
- When the promise is eventually fulfilled, `then` receives the resulting value and calls the function, passing it this value as an argument.
- The function can return any value (numbers, strings, or any other data).
- Finally, `then` returns another promise with the value returned by the function.
- `catch`
- `catch` expects a function argument, which will be called when the promise encounters an error.
- When the promise encounters an error, `catch` receives an error object and calls the function, passing it this error as an argument
npm install axios
axios is a library that also sends requests, but unlike request, where you pass in a function as the second argument, axios returns a promise, and you pass the next function into the promise’s then function. Instead of passing a function as a second argument, you pass the function as an argument into the return promise’s then function.
npm install node-fetch
The browser provides you with a function called fetch that allows you to send requests and returns a promise.
<h1 class="display"></h1>
<script>
const display = document.querySelector('.display')
fetch('https://c0d3.com/api/lessons').then( (res) => {
return res.json()
}).then( (data) => {
display.innerText = `there are ${data.length} lessons`
})
</script>
Note that when using fetch in the browser we don’t need to require it; the browser supports fetch automatically. Because the browser only supports fetch, software engineers prefer to use the node-fetch library instead of axios when writing code to run on the computer.
If someone tells you they know ajax, it means that they know how to send requests from the browser. If you are able to run the examples above on the browser, it means that you know how to use fetch to send a request from the browser. So you can now claim that you know ajax too!
Many times you may want to send more than one request at once. To do this, you can use Promise.all, which takes in an array of promise objects and returns a promise. When the then function runs, the argument function will get an array of responses.
const fetch = require('node-fetch')
const pokeNumbers = [
37, // vulpix
38, // ninetales
39, // jigglypuff
40 // wigglytuff
]
const arrayPromises = pokeNumbers.map( (num) => {
return fetch(`https://pokeapi.co/api/v2/pokemon/${num}`).then((result) => {
// result is an array of response objects, one for each request
// we need to parse the JSON in each result
return result.json()
})
})
Promise.all(arrayPromises).then((results) => {
// results is now an array of objects
// we can do something with it, like
results.forEach((e) => {
console.log(`${e.name} weighs ${e.weight}`)
})
})
A mock function is a fake function that doesn’t do anything but keep track of how many times it called. It also keeps track of the arguments that it is being called with. You can create a mock function by calling jest.fn function.
The mocked function contains an array of all the times the function has been called. You can access this array like this:
// a is a mock function.
const a = jest.fn()
a() // a is run without any arguments.
console.log( a.mock.calls.length ) // a.mock.calls is an array of length 1
a('hello', 'world') // a is run with 2 arguments.
console.log( a.mock.calls.length ) // a.mock.calls is an array of length 2
a('test', 1, 9, 100) // a is run with 4 arguments.
console.log( a.mock.calls.length ) // a.mock.calls is an array of length 3
a( () => {
console.log('hello')
} ) // a is run with 1 argument
If you want a more complicated mock function that can return something, you can pass in a function.
const a = jest.fn( (a, b) => {
return a + b
})
console.log( a(6,7) ) // prints out 13
Example, We are testing a function that takes in a number and prints out “hello” that many times.
// solution.js
const logHello = (i) => {
if (i <= 0) return
console.log('hello')
return logHello(i-1)
}
module.exports = logHello
// solution.test.js
const logHello = require('./solution.js')
describe('logHello', () => {
test('should log 0 times if -10 is passed in', () => {
console.log = jest.fn()
logHello(-10)
expect(console.log.mock.calls.length).toEqual(0)
})
})
Sometimes, the functions you are testing will depend on external modules that you have no control over. To mock an entire module, you run jest.mock(‘module-name’)
In the example below, the function first sends a request to an API to get an array of objects. It will console.log the title for each title property of the object in the array.
// solution.js
const request = require('request')
const logTitles = () => {
request('https://c0d3.com/api/lessons', (err, response, body) => {
const arr = JSON.parse(body)
arr.forEach( (lesson) => {
console.log(lesson.title)
})
})
}
module.exports = logTitles
// solution.test.js
jest.mock('request') // very first thing to do, mock the library
// when this happens, this file will get the fake request library
const logTitles = require('./solution.js')
// We need to change the mocked library so we can send back fake data
const request = require('request')
// solution.test.js
const logHello = require('./solution.js')
describe('logHello', () => {
test('should call request 1 time', () => {
request.mockClear() // we should always reset the mock of modules
console.log = jest.fn()
logTitles()
expect(request.mock.calls.length).toEqual(1)
expect(console.log.mock.calls.length).toEqual(0)
})
test('should log 2 times if response has 2 objects', () => {
request.mockClear() // we should always reset the mock of modules
// we need to stringify it because request callback expects a string
console.log = jest.fn()
const fakeResponse = JSON.stringify([{
title: 'hello',
}, {
title: 'world'
}])
logTitles()
// request is called 1 time with 2 arguments.
// The second argument is a function. Let's run that function
request.mock.calls[0][1](null, null, fakeResponse)
expect(console.log.mock.calls.length).toEqual(2)
expect(console.log.mock.calls[0][0]).toEqual('hello')
expect(console.log.mock.calls[1][0]).toEqual('world')
})
})