Functions are the main component of Javascript. It is so important that an entire chapter is devoted learning the intricacies of functions. Remember that functions are a block of code neatly packaged into a single unit.
In the previous lesson we learned that you can define a function using the keyword function
for example: function add(a, b) { return a + b }
In Javascript you can also assign this function to a variable using the var keyword.
var myAddFunc = function add(a, b) { return a + b }
now you can invoke the add function using myAddFunc(a, b);
we have been using parameters for a while now, but they are exactly like variables, but the important difference is that they are passed into the function rather than being created inside the function scope.
Scope is a very important topic,
When a function is created it creates it's own local environment and we define that as scope.
This means that variables that are defined inside of this scope is not available to the outside of this scope.
Lets go through some examples:
var x = 1;
var myInsideFunction = function() {
// Notice that we have already used x above as a variable name
var x = "5";
console.log("I'm Inside", x);
}
myInsideFunction();
console.log("I'm Outside", x);
var changeOutSideVarFunc = function() {
// we will grab the above scope x and change the value;
x = 10;
}
// invoking the changeOutSideVarFunc will change the value of x from 5 to 10
changeOutSideVarFunc();
console.log("I'm Outside", x);
// However the x in the myInsideFunction will not be changed
myInsideFunction();
I would say that scopes are one of the main stumbling blocks for new programmers so please read through this example until it is crystal clear :)
In Javascript you can also nest scopes as deep as you want.
function levelOne() {
var canYouSeeMe = 'yes';
function levelTwo() {
console.log('levelTwo invokes me');
function levelThree() {
console.log('levelThree invokes me');
console.log('i can see', canYouSeeMe);
}
levelThree();
}
console.log('levelOne invokes me');
levelTwo();
}
levelOne();
Note that if you were to try and call the levelTwo function at the end of the code block, it would throw a undefined error because levelTwo is not in scope at that level.
However, if you were to try and access the variable canYouSeeMe from the levelThree function, you are able to do it.
This is because functions have access to variables and methods that the containing scopes have. This also propagates all the way up to the global scope.
There is small caveat in Javascript when it comes to function declaration.
Lets take this example where I try to invoke a function that is defined at a later time in our code
willThisWork();
function willThisWork() {
console.log("why does this work?");
}
This works because in Javascript functions declarations are not part of the regular top-to-bottom flow control instead they are hoisted up to the top of their scope.
Although this is possible I would advise against using this pattern. Instead make sure to try your best to declare you functions first and then invoke it at a later time.
I feel like the call stack is important enough to warrant its own section. For now I've linked a Youtube video that I believe is worth watching: https://www.youtube.com/watch?v=8aGhZQkoFbQ The entire video is about how Javascript works, but the callstack portion starts around 4:10
When you define a function with arguments, you are essentially creating a contract with the outside scope stating that this function takes X amount of arguments.
However, Javascript doesn't really care about your contract. In Javascript passing the correct amount of arguments is optional. If you pass in more arguments than the function takes, the function will simply assign the arguments as undefined and move on its merry way.
The upside to this odd behavior is that you can create default values for your arguments when no value is passed.
function rollDice(face = 6) {
console.log(Math.floor(Math.random() * face) + 1)
}
The rollDice can now roll different face sided dices such as a D20, rollDice(20);, but if you don't supply a argument it will just roll a D6 by default.
Closure is a technique that is based on two interested behaviors of functions in Javascript
- functions can be values
- local variables are "re-created" every time a function is called
Let's see an example and go over it.
function mail(sender) {
var name = sender;
return function(recipient) {
return recipient + ' received mail from ' + name
}
}
var sendMailTo = mail('John');
var mailMsg = sendMailTo('Mike');
console.log(mailMsg);
There are few subtle things that you should notice from the example.
-
In the
mailfunction we are returning a anonymous function that returnsrecipient + ' received mail from ' + name. Which is a string concatenating therecipientvariable and thenamevariable. -
when we assign the
sendMailTovariable we invoke themail()function, therefore, assigning the return value offunction(recipient) { return recipient + ' received mail from ' + name }as the value; -
This means that when we invoke assign
mailMsgwe are also invoking thesendMailTo('Mike')which holds a function as a value. Note we are also passing a string called'Mike'which the function will store asrecipient -
finally the end return value is a string:
Mike received mail from John
The important concept here is that the variable name was accessible even after the mail('John') function was invoked during the assignment of sendMailTo = mail('John'). This is what closures are. It allows your to wrap local variables in a function to be used at another time outside of their scope.
This is a pretty long handed explanation of this, but closure is a very important topic that most beginning programs will not get the first time around.
Recursion is simply when a function calls itself. One thing to note is that in most cases problems you can solve with recursion you can also solve iteratively. However, both methods will have their trade offs. recursion usually offers a elegant solution and also allows you to solve problems that require exploring or processing several branches. However, recursion is much more slower than it's counter part. e.a. loops.
Lets go through a quick example by writing a functions that returns n factorial.
function factorial(n) {
if (n === 1) {
return 1;
}
return n * factorial(n - 1);
}
console.log(factorial(5));
factorial(5) will log 120; Note that the function factorial(n - 1) is called inside the factorial() function. The function calling itself makes this function a recursive function.
Solving problems using recursion is not the easiest thing to do in the beginning. However, here are few tips to get you started.
- Think about the base case first. For our factorial problem, the base case is
if (n === 1) {
return 1;
}
This just means that this is the last point at which the function will not call itself anymore.
- recursively call your function with the next value. This part is hard said then done. In our example it is
return n * factorial(n - 1). In Math to solve one step of the factorial problem, you multiply the currently number with the previous number.
We will have more opportunities in the future to solve many recursive problems :) Look forward to it.
In this short challenge you will be required to define functions in multiple ways.
Steps:
- Create a function with the name
myNamewhere you pass in the argumentnameand log the name to the console. Invoke themyNamefunction. - Assign a function to a variable name
favoriteFunc. Invoke yourfavoriteFunc. - Create a function
powerBywith optional parameter that powers the base number by2by default. Try powering a number with the default value and also without.
We will be practicing going in and out of scopes. Remember scopes are very important to understand so take time to really make this understanding your own.
There are some issues with the following code base. Things are not logging the way they should be. Fix the scope issues to get the problem to function as intended. You may need to change some argument names as well. Good Luck!
var a = 5;
var b = 3;
function getUserInput(aNum, bNum) {
console.log('value one is', a);
console.log('value two is', b);
function add(aNum, bNum) {
var a = aNum;
var b = bNum;
function checkNumber(a) {
if (typeof a === 'number') {
return a;
}
var sum = checkNumber(a) + checkNumber(b);
}
return sum;
}
}
return add(a, b);
console.log(getUserInput(a, b));
}
- Fix the below loop to properly print out the i number in order. You must keep the setTimeout function
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000 + i);
}
- Create a closure that allows you to execute the below code and return the expected values
var addFive = createBase(5);
addFive(2); // returns 7
addFive(10); // returns 15
In mathematics, the Fibonacci numbers are the numbers in the following integer sequence, called the Fibonacci sequence, and characterized by the fact that every number after the first two is the sum of the two preceding ones
The sequence would look something like this.
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...
below is the iterative version of the Fibonacci sequence.
Your challenge is to write it in a recursive manner. :)
function iterativeFibonacci (n) {
// default values for the first fib sequence 0, 1, 1
var a = 0;
var b = 1;
var c = 1;
for (var i = 2; i <= n; i++) {
// calculate the next Fibonacci sequence
c = a + b;
// update the base of a up one number
a = b;
// update the base of b to the new sequence
b = c;
}
return c;
}
console.log(iterativeFibonacci(10));
Try your best to figure this out on your own, but you can always look it up. Just make sure you understand how what the base case is and the specific algorithm that you are recursively calling!.
Kyle Simpsons Article on Lexical Scope and Closures: https://medium.com/@nickbalestra/javascripts-lexical-scope-hoisting-and-closures-without-mystery-c2324681d4be