Passing by value vs Passing by reference.
Javascript only has passing by value.
First Class and Higher Order Functions
First-Class Functions
- JS treats functions as first-class citizens
- This means that functions are simply values
- Functions are just another “type” of object
Therefore they can be stored in objects and passed as parameters in other functions.
There are methods on functions (such as .bind())
Higher-Order Function
- A function that receives another function as an argument, that returns a new function or both
- This is only possible because of first-class functions
btnClose addEventListener('click', greet) // higher order function (addEventListener); first-class function (greet)
const myFunction = function(str, fn) // Callback function
Callback Functions
Allow us to create abstraction.
Vital part of the JavaScript
Call, Apply & Bind Methods
.call() is a method that can be applied to the function which sets the ‘this’ keyword (passed in the first argument).
.apply(thisArg, argsArray) is the same as above however it requires an array as the second argument.
…however with the spreadoperator there is no need to use the .apply method, just use .call(thisArg, ...argsArray) as it does the same thing.
The bind() method works like the .call() method but returns a new function. The new function is bound to the new object. You can also pass arguments after the thisArg to set the default parameters (partial application).
The bind method is useful when used with Event Listeners
lufthansa.planes = 300;
lufthansa.buyPlane = function() {
console.log(this);
this.planes++;
console.log(this.planes);
};
// NaN
document.querySelector('button.buy').addEventListener('click', lufthansa.buyPlane) // this would be the buy button and therefore not work
// 301
document.querySelector('button.buy').addEventListener('click', lufthansa.buyPlane.bind(lufthansa);
Partial application
const addTax = (rate, value) => value + value * rate;
const ukTax = addTax.bind(null, 0.2); // this sets the value to a default
Coding challenge
'use strict';
///////////////////////////////////////
// Coding Challenge #1
/*
Let's build a simple poll app!
A poll has a question, an array of options from which people can choose, and an array with the number of replies for each option. This data is stored in the starter object below.
Here are your tasks:
1. Create a method called 'registerNewAnswer' on the 'poll' object. The method does 2 things:
1.1. Display a prompt window for the user to input the number of the selected option. The prompt should look like this:
What is your favourite programming language?
0: JavaScript
1: Python
2: Rust
3: C++
(Write option number)
1.2. Based on the input number, update the answers array. For example, if the option is 3, increase the value AT POSITION 3 of the array by 1. Make sure to check if the input is a number and if the number makes sense (e.g answer 52 wouldn't make sense, right?)
2. Call this method whenever the user clicks the "Answer poll" button.
3. Create a method 'displayResults' which displays the poll results. The method takes a string as an input (called 'type'), which can be either 'string' or 'array'. If type is 'array', simply display the results array as it is, using console.log(). This should be the default option. If type is 'string', display a string like "Poll results are 13, 2, 4, 1".
4. Run the 'displayResults' method at the end of each 'registerNewAnswer' method call.
HINT: Use many of the tools you learned about in this and the last section 😉
BONUS: Use the 'displayResults' method to display the 2 arrays in the test data. Use both the 'array' and the 'string' option. Do NOT put the arrays in the poll object! So what shoud the this keyword look like in this situation?
BONUS TEST DATA 1: [5, 2, 3]
BONUS TEST DATA 2: [1, 5, 3, 9, 6, 1]
GOOD LUCK 😀
*/
const poll = {
question: 'What is your favourite programming language?',
options: ['0: JavaScript', '1: Python', '2: Rust', '3: C++'],
// This generates [0, 0, 0, 0]. More in the next section 😃
answers: new Array(4).fill(0),
registerNewAnswer() {
let response = Number(
prompt(
this.question +
'\n' +
this.options.join('\n') +
'\n(Write option number)'
)
);
console.log(response);
if (response >= this.options.length || response === NaN) {
alert('Write option number');
this.registerNewAnswer();
} else {
// update answers array
this.answers[response]++;
this.displayResults(this.answers);
}
},
displayResults(type = 'array') {
if (type === 'array') {
console.log(this.answers);
} else {
console.log(`Poll results are ${this.answers.join(',')}`);
}
},
};
document
.querySelector('.poll')
.addEventListener('click', poll.registerNewAnswer.bind(poll));
const pollResponses = [5, 2, 3];
poll.displayResults.call({ answers: pollResponses }, 'string');
Immediately Invoked Function Expression (IIFE)
(function() {console.log('one time function')})();
(() = console.log('one time function'))();
All data inside IIFE is private and encapsulated.
Closures
A closure makes the function remember all the variables that were created at the function’s birthplace. If a variable is reachable via closure then it is not garbage collected from the Heap.
A closure gives a function access to all the variables of its parent function even after that parent function has returned. The function keeps a reference to its outer scope, which preserves the scope chain throughout time.
const secureBooking = function () {
let passengerCount = 0;
return function () {
passengerCount++;
console.log(`${passengerCount}passengers`);
};
};
const booker = secureBooking();
booker();
booker(); // passengerCount still increments because closure.
console.dir(booker); // will show closure in the scopes