Functions and Closure And Scope
Closures represent code and its ability to access other parts of code. Closure is a result of different scopes at play.
- Functions and Closure And Scope
Scope Is About What Is Available Within Reach
Maybe like life, something are in-scope and some things are out-of-scope.
Changing a tire on a car means certain things are in scope: using a tire-iron, a jack, dealing with lug nuts, potentially a tire-pressure gague and a pressurizing device. The task, in this case, defines the scope. There is a broader scope, perhaps the drive-train, whic encapsulates the tires, but also includes more. There is an even broader scope, perhaps the entire vehicle, which summarizes all of the child components.
JavaScript has scope, too.
JavaScript Has A Global Scope
When running JavaScript, the global scope includes everything.
Front-end developers may not be intimately familiar with all of the details of what is available in the global scope of a browser context.
Server-Side developers may not be intimately familiar with all of the details of what is available in the global scope of a node context.
JavaScript Has A Module Scope
JavaScript code can be run in a module. Code in a module is scoped to that module.
JavaScript Has A Function Scope
JavaScript code can be run in a function.
Code inside the function is scoped to that function.
This functional scope is critical to master for implementing a funcitonal programming style.
Closures Represent Scope Encapsulation
Take for example a function that is part of a process:
let a = 123;
function addTwo(a,b){
console.log("a inside addTwo: ",a)
return a + b
}
let result = addTwo(2,3)
// a inside addTwo: 2
console.log(result)
// 5
console.log(a)
// 123
function addThree(x,y,z){
console.log("a inside addThree: ",a)
return x + y + z
}
console.log(addThree(4,5,6))
// a inside addThree: 123
// 15
Functional Closure Takes Precedence Over Global Scope Variables
Let's start by looking at the addTwo
function.
In this trivial example there are a few things to note: the global scope, the function scope, and the variable(s) a
.
The global scope has a few pieces:
- the
a
variable declaration - the
addTwo
variable declaration - the running of
addTwo(2,3)
stored in a variable calledresult
- the instruction to
console.log()
theresult
value - the declaration of the
addThree
- the instructions to log the results of the
addThree(4,5,6)
(addThree is covered in more depth below)
The function scope has a few pieces:
- "knowledge" of 2 arguments of the function, here named
a
andb
- the instructions to log the
a
parameter - the instruction to return
a + b
as a result of running this function - these parts within the function represent the function's closure
The a
variable is the critical piece to master when understanding scope and closure:
- in the global scope, a is equal to
123
- in the function's closure,
a
is equal to whatever the first parameter passed to the calling of theaddTwo
function - in the case above, it is equal to2
- the value of a inside of addTwo only exists as the argument value, not as the global value
What about addThree
? See below
Global Scope Is Available In A Function Closure
Let's consider the addThree
function, pand particularly how it differs from the addTwo
function.
The global scope is shared between the addThree
and addTwo
functions.
The function scope is a bit smaller:
- there are 3 args of the function,
x
,y
andz
- there are instructions to log the
a
variable - the
a
variable is not declared inside theaddThree
function and the function "gets" thea
variable from the "next scope" available to the function. Because thea
variable is not declared as an arg in thisaddThree
function like it is in theaddTwo
function, thea
variable is "found" by theaddThree
function in the global scope. - these parts within the function represent the function's closure
- a critical detail here is that the closure of this
addThree
function refers to thea
global-scope variable. In pure functional programming, this is a bit of a "no no"
- a critical detail here is that the closure of this
Perhaps Use Composition Over Inheritence
Inheritence Can Inherit Complications
The problem that inheritence brings with it's design is when a "low-level" (or high level, base, whatever you feel like calling it) inherited object needs changing, this can make "child" or inherited instances of the object different than expected.
An example is that a multi-"level" class structure exists, and a class early on in the chain needs adjusting...
- a "card" class builds a card component
- it includes a title and some content
- a "chart" class inherits the card class, and takes as a prop a chart-type (and does chart things with it)
- a barChart class inherits the chart class, and requires bar-chart details
- a table class also inherits the card class, and does table-things with the card
Perhpas over time the table class expects to be full-screen-able. In order to get this out the door to customers, this is included in the table class.
Perhaps over time, this same expand/collapse feature is requested on all "card" instances (with more customer-friendly language).
Here lies the problem that inheritence brings along with it: does code get copied+pasted? Does code get cut + moved? How will every other instance of a "card" be expected to act? If this is delivered incrementally (which might be the expectation), the implementation may get even more complex.
Composition Can Introduce Flexibility
With composition (more to come on this later), the building of objects can be less "dependent" on each "parent" element && rather, well, a composition of details.
One great one-liner comparing the two approaches:
In general, inheritance could answer the “is-a” relationship, when composition “has-a”.
An inherited class "is a" extension of another class which includes methods and state.
A composed function "has a" bunch of methods and state, and is not an instance of other similar instances.