Section 3 Part 2 - Closures and Callbacks
Syntax Parsers
Before your code becomes executed, there is an intermediate step it goes through where your code becomes parsed to determine valid syntax.
It may even change your code.
In js, there it is not technically nessessary to have semicolons.
This is because javascripts engine will do this for us.
However, it is always recommended to be explicit with the code you write, and that includes typing down your own semicolons - because it can lead to unintentional mistakes.
Here, the syntax parser saw return, and then the carriage return. So because of this, it included and semicolon, and nothing was returned.
So, basically, this system enforces a particular kind of way of writing out this. So that you avoid these kinds of mistakes. I dont know if this is good or not. But this will fix it.
Whitespace
Javascript is very liberal in the way they allow whitespace. But their was something that I disagreed about with the instructor in this lecture. He recommended to comment out your code as much as possible. I agreed, in a way, but they way he demonstrated in the video was highly redundant, obvious and unnecessary.
I would only comment if it makes my code more productive. Doing something like this is not good.
IIFE's and Safe code
An IIFE is a quick way to execute functions on the fly.
Here, we have (), so it expects some kind of execution.
Just as if we were to do greet('dan'); - here greet is simply just replaced with the function.
Like inline in C++
But the main reason this is done is mainly is the safety is provides.
With this kind of immediate execution, we avoid polluting the global namespace.
Our code is kept clean, and our variables belong only where they need to belong.
And when we want to access the global namespace, we can. We just have to pass the global window object as a reference, and modify whatever we want to modify.
This is very clean because we only affect the global namespace when we want to do so intentionally.
Understanding Closures
Lets learning another critical part of JS.
What's happening.
We've invoked a function that returns an object, or function.
Because another function was returned, it could be invoked immediately.
() <-- invoke
greet() <--- invokes function
greet()() <--- invoke what was returned
It can also be invoked uniquely like this:
This is essentially the same thing. A function is stored in to a dynamic variable, and then invoked.
But how greeter remember 'hi' previously on the stack, has to do with closures.
Whats happening under the hood:
greeter is under the global namespace
greet('hi') creates a new execution context, with variable 'hi' stored in memory.
Whenever a function returns it's stack memory is removed.
Eventually js will clear out the memory, but in that moment, the variable 'hi' still exists there, and nothing had overwritten it.
We move back to the global context, where a another execution context is created with greeter(), along with allocated memory for 'dan'.
When the function gets executed, 'name' is found natural - as it was supplied as a parameter, but what about greeting? Because the function returned, it was removed from the stack frame.
However - as we've noted before, it still exists in memory, and when the function executes, it begins to look up the scope chain to find it.
The idea of closure, is that any function within a function has reference to it's memory, even if was released from that stack.
This is done by the javascript engine on its own. This idea or concept is reliable 100% of the time.
Understanding Closures Part II
There is a classic example of a demonstration of closures.
Initially, I expected 1 2 and 3 respectively in this output, but soon I've realized why the output is the way it is.
Here are my thoughts:
storedArray = global variable
classic() <-- new execution context
Stack frame becomes terminated, but variables remain in memory.
New execution context for storedArray()[] invoked calls.
What tricked me was the console.log(i) part. Here, the function is not being executed. We are ONLY supplying the array with a new function object. The variable i is being passed to it as well. 'i' is incremented, but the way the code executes to begin with is that it looks for that variable 'i'.
When we then invoke storedArray; what we are going is creating a new execution context.
The variables are essentially lost, but they still remain in memory.
When we actually execute the function, we look up the scope chain to find 'i' in order to console.log it.
In its process, i had been rewritten 3 times, and when the function call finished, we've ended up with the latest 'i'. So the console prints 3.
The execution context was the same for all of the object creations. Thats why when it referred back up the scope chain, it found i as 3, each time.
How can we fix this?
The way to do with create a seperate execution context for call. We can do this through IIFE's, which execute the function immediately.
We push to an array that executes its own execution context. But the execution of this function returns an object, so we are able to invoke it whenever we want.
The main reason this works is because we create a new variable each time in memory, and when we invoke it, will look back to that unique memory spot in memory. No more looking at the same place, like before.
Function Factories
Another great demonstration of function closures
The take away from this is that we've defined a function that creates two seperate contexts depending on the input given.
Because we have two different return scenarios, there are 2 different contexts in which we can use closure to search up the scope chain.
When we execute var greetEng = factoryGreetign for both spanish and english, we create two different memory spaces.
Both, however, where returned from the same anonymous function.
When we executed it a second time with the returned object the language variable remained remembered.
Function Callbacks
A functional callback is when you supply one function another function that runs after the first execution.
setTimeout demonstrates what ever after 3 seconds, we still maintain access to variable out the immediate scope, (greeting).
Call(), Apply(), and Bind() Functions
All functions have access to these methods.
First bind.
We can immediately see that this.getfullname doesnt make sense because this is not defined under the person method.
What if we had a way to change this? This is what the bind keyword does.
Remember, this is an object that changes on context method.
Certainly we can use
But I've suspect binder is clearer in large circumstances.
bind creates a copy of the function
Call preforms something similiar. Rather than making a copy, like bind does, it executes the function on the spot.
In addition, because it contains the ability to execute, you can supply it with parameters.
Apply performs that same exact this as call, except for a single difference.
That, is after supplier your this point, the parametes are references in array, which can be beneficial under mathematical circumstances.
So call and apply can be used interchangable depending on how we want to use it.
Function borrowing and currying
What if we had two similiar object methods, but the objects are explicitly different. Call, bind, and apply can also help us do this, and remove redudent code.
1). We access person.getfullname with full rights
2). We invoke that function and bind 'this' to person 2
3). Now a stranger function has access to a method its not defined under.
Function currying is when you present some default parameters to a function. This is what happens when we pass in parameters with the bind keyword.
This is different from before when we 've used coersion. This method would specify a variable if it did not exist. Function currying copies and function, and then specifies a perminate default value.
This is essentially just a shortcut, but it also demonstrates the effectiveness of bind. Because a copy is created, and not executed, the copy can be strictly used in situations were we need them.
Functional Porgramming
Think and code in functions
Like with all programming languages, are are sloppy ways of doing things. The more we write clean and maintainable code, the fewer bugs, and better chance we have at maintainer large scale programs.
With js, we want to take advantage of its functional programming capabilities.
Consider the following bad code
Lets see if we can improve this
Much better. Lets track our improvements.
Created a generalized method for doubling any array
Ultilizaed functional programming to defined a parameter as a function.
Let allows use to more dynamically get to our goals.
Functional parameters can change whenever we want them to, and it cleans up a bit work.
For example, by simply modifying our external function, we can define a whole new functionality.
One of the powers that comes with functional programming is the ability to take any complex, large scaled function, and break it down into as many pieces as you'd like.
For example, to avoid smart numbers, we can write our own function like this.
Then we can call it like this.
Albiet, I dont really see this as a strong improvement.. What we've done is bind the number 1 as a perminate parameter into a function call, and then called it.
Really, we've just moved 1 number to a different location.
I suppose its a bit nicer. We have our function methods, and our calls are very organized.
One more thing we can do. Suppose that we wanted to avoid writing 'this' all the time. To solve this, we can bind 'this' to the function call.
Key to functional programming: Using functions as parameters, and defining those functions as a set of smaller functions.
With the end goal in mind being that you call the function in a single line of code, without any added complexity.
Notice how dynamicCheckSimplified was called with a simple parameter. It became simply defined, and reusable.
It is also important to know that in the process of breaking down your code more and more it best not to mutate or change any of the code in the process. That is, we want to avoid using smart numbers as much as possible.
So it is important to keep in mind that we are simply writing out functions that transform it from one form into a next that suits are need.
If we we're to use smart numbers, we may find ourselves moving back up, and change things around whenever we need them.
So essentially, we want the functions to be the same, but simply change shape to fit as a key to our puzzle.
Functional Programming Part 2
Underscore.js <-- open source js library.
Functions and varibles are invoked with an underscore.
At a glance
Last updated
Was this helpful?