JavaScript's functions are first class, they can be treated as any other variables. That is they can be stored in variables
In this example we create an object my_console
which has a copy of two functions inside it, print()
, which is just a standard console.log
and print_money()
which adds a built in formatting function to display the output as US currency.
Like any other function call we need to add a set of parenthesis to execute the function.
In this example we have an object other_console
which we add a print
property to. The value of this print property will be the function my_console.print
We can then call that function using other_console.print()
with parenthesis
Another way to do this:
We have 3 categories of scope in JS:
Variables are defined to be global when it is used without an initial definition.
As you can see they can be changed anywhere and everywhere, this becomes much worse when we start working asynchronously and a variable can be changed silently a few seconds after some delayed callback executes.
Function scope variables are defined when the var
keyword is used. The scope of these variables includes their function body and their parent's function body. Access from outside of these is not allowed.
x
is not accessible from outside parent()
Both foo
and bar
can access variable x
, but only foo
can access variable y
. Once foo returns the reference to y
is no longer available.
With function scope variables are scoped to functions, but not other blocks (like conditional statements / loop)
What happens under the hood is that these declarations are moved to the top where they get declared, they all get the initial value of undefined
and variable y
never has that changed.
Variables use lexical scope when declared with the let/const
keyword.
Variables are scoped to the block in which they are defined. They can access parent scope variables.
Uncomment these to see each of the errors,
Note programs in JavaScript do not run beyond their first error.
Lexical scope describes the behavior of nested functions. In the above example print()
is nested inside of foo()
. When this occurs, the inner function has a link to all of the variables available in it's parents scope.
Any changes made while inside the inner function persist even after the function is no longer in scope.
In the above example bar
decreases b
in this scenario and this change persists when print()
is called.
Lexical Scoping defines how variable names is resolved in nested functions
inner functions contain the scope of parent functions.
A closure adds to this, it is the notion that this continues to apply even after the parent function has returned or is no longer in scope.
A higher order function is function that accepts a function as input or returns a function as output. (This one does the latter)
Calling ask()
returns a function which we assign to variable whoquestion
.
This function ask()
checks the type
of question, and returns back a function corresponding to that type.
Notice when we make a call to whoquestion()
on the next line, we are no longer in the scope of ask()
. Yet we can still are able to print out the contents of mytype
. This is what closure provides us, access to variables from a parent scope (ask
) even if the parent function is no longer in scope.
We cannot access the variable mytype
directly however.
(Note this demo uses variable mytype
to make it explicit that this value would normally would be out of scope, in actual applications it is better to just use type
and not create a new variable)
A closure is when a function is able to access it's lexical scope even when that function is executed in a different scope.
A common mistake is to treat this as a snapshot.
Closure is a preservation of the linkage of the variable, it is not a snapshot of the values.
You close over variables, not values. If you make changes to those variables, those changes persist.
In this example our call to use_foo()
prints out B
, proving that it is not a snapshot but a linkage.
Closures are used heavily in asynchronous programming (our next topic) to preserve our ability to access variables after a long delay has occurred, but they are also useful in scenarios where we want to hide implementation details while still revealing an interface.
This is sometimes called a factory function.
We can create "class" like structures in JavaScript with closures, no need for the this
keyword
Notice the return statement, It is returning back an object with all our "public" methods,
The method display_inventory
is treated as private and while it can be used internally, our application cannot call it directly outside of the factory function.
This is same with name
, age
, and inventory
.
There will be a bit more that needs to be explained regarding closures, that will be discussed after we introduce asynchronous programming with callbacks.