A walk in JavaScript
DAY 6
- Functions
- General Definition
- Function declaration (function statement)
- Function expression
- Function constructor
- Constructor vs declaration vs expression
- Properties of the Function object in the prototype chain
- Arity & formal parameters
- Formal parameters and the
arguments
thing - Functions as properties of an object
- IIFE
- Pure functions
- Side Effects
- Execution context
- Types of Execution Context (executable code)
- Execution Stack
- How Execution Context is defined?
- Articles and books used for this section
- Scope
- Part of a program
- ECMAScript definition
- General definitions
- Examples
- Hoisting
- Closure
- General definition
- Examples
- Can we Cheat Scope?
-
ev[a i]l - with
-
- Relative Concepts Readings
- Preliminary practice
- Exercises
Functions
General definition
Let’s start with a general definition to understand what a function is:
In computer programming, a subroutine is a sequence of program instructions that performs a specific task, packaged as a unit. This unit can then be used in programs wherever that particular task should be performed.
Subprograms may be defined within programs, or separately in libraries that can be used by many programs. In different programming languages, a subroutine may be called a procedure, a function, a routine, a method, or a subprogram. The generic term callable unit is sometimes used.
Source: Wikipedia
Nice, we now know that a function
might contain instructions to do something and is “callable”.
We also know from our previous lessons that a function
is a Function Object with a complex definition:
ECMAScript function objects encapsulate parameterized ECMAScript code closed over a lexical environment and support the dynamic evaluation of that code. An ECMAScript function object is an ordinary object and has the same internal slots and the same internal methods as other ordinary objects. The code of an ECMAScript function object may be either strict mode code (10.2.1) or non-strict mode code. An ECMAScript function object whose code is strict mode code is called a strict function. One whose code is not strict mode code is called a non-strict function.
Source: ECMAScript International
Lot of terms are completely obscure at this time but we will shed light to them as the day grows.
Now let’s see if MDN has something more to say:
In JavaScript, functions are first-class objects, because they can have properties and methods just like any other object. What distinguishes them from other objects is that functions can be called. In brief, they are Function objects. Source: MDN
That’s a great reminder of something important. In JS, functions are first-class citizens, therefore they can:
- be assigned to a variable
- be formal parameters of a function
- be returned as result of a function
- be tested for equality ( therefore implicitly they can be modified )
Now let’s start digging with the following topics:
Function declaration (function statement)
A function declaration or function statement defines a function with a name and arbitrary number of specified parameters. It might or might not contain statements on its body and it’ll implicitly return
undefined
unless an explicit value is defined on the return ...;
statement.
function name([param[, param,[..., param]]]) {
[statements]
}
Function expression
A function declaration can be defined inside an expression like a assignment expression where the function is assigned as a value for a given variable. In the case of a function expression the name of the function can be omitted resulting in two variants, named function expression or anonymous function expression; the differences between the two are significant and we’ll see them soon.
var myFunction = function [name]([param1[, param2[, ..., paramN]]]) {
statements
};
Function constructor
Function objects created with the Function constructor are parsed when the function is created. This is less efficient than declaring a function with a function expression or function statement and calling it within your code because such functions are parsed with the rest of the code.
Source: MDN Function Constructor
new Function ([arg1[, arg2[, ...argN]],] functionBody)
Constructor vs declaration vs expression
Even though they will end up producing a function, there are several differences between them, some subtle and some coarse. Let’s see some examples in MDN - Constructor vs declaration vs expression
Properties of the Function object in the prototype chain
As we already learn, a function
is an object
, therefore it benefits from the prototype
chain defined properties, which can be any of the ECMAScript types we know.
Let’s take a look at MDN - Function prototype object
Arity & formal parameters
Arity is the term used to describe the number of arguments
a function or operation accepts. They can be described as nullary (0 arguments), unary (1), binary (2) and so on.
Intrinsically, functions in JavaScript are variadic ( a.k.a multigrade, n-ary, polyadic …), it means that even though you can define the formal parameters
at function declaration time, it will accept AND handle less or more arguments
. That opens a whole world of interesting implementations.
Example from Wikipedia - Variadic function in Javascript
function add(n, i) {
return n + i;
}
function sum(...numbers) {
return numbers.reduce(add);
}
sum(1, 2, 3) // 6
sum(3, 2, 1) // 6
Example of LHS and RHS analysis
/**
* How many LHS look-ups can you see?
* How many RHS look-ups can you see?
*/
function speakToMe(input) {
var helperVal = input;
return input + helperVal;
}
var result = speakToMe( 10 );
Formal parameters and the arguments
thing
Did you note something weird about those terms arguments
and parameters
that seem to be interchangeable but … not quite? Well, you’re not alone. It turns out the ARE kinda vague, so for clarity well see a couple of definitions here:
In computer programming, a parameter or a formal argument, is a special kind of variable, used in a subroutine to refer to one of the pieces of data provided as input to the subroutine. These pieces of data are the values of the arguments (often called actual arguments or actual parameters) with which the subroutine is going to be called/invoked. … Unlike argument in usual mathematical usage, the argument in computer science is thus the actual input expression passed/supplied to a function, procedure, or routine in the invocation/call statement, whereas the parameter is the variable inside the implementation of the subroutine.
Source: Wikipedia - Parameter
In mathematics, an argument of a function is a specific input to the function; it is also called an independent variable.
Source: Wikipedia - Argument
In short, the parameters
are the signature, or the variables you define at function declaration time, and the arguments
are the values
you pass to the function at function call time.
That said, arguments
itself has a special meaning in JavaScript. Now let’s see more about how JavaScript handles arguments, provides a new way in ES6 to handle those arguments in a consistent way, and provides a feature to define default values for the formal parameters (like other languages do)
- The
arguments
thing ( deprecated 👎) - Rest parameters
- Default Parameters
Functions as properties of an object
We’ve learned the concept of first-class citizens, therefore we can assign a function to a variable …. what if that variable happens to be an object property? Now we’re talking, we could create an object with many values of any ECMAScript valid type, including functions, that might manipulate values of the same or other objects!!!. This functions are sometimes called “methods” but that might be an arguable statement. (the door for Object Oriented Programming has been opened )
Let’s get some insights from MDN - Method definitions
IIFE
Do you remember what an expression is? Now what if I tell you there’s a particular expression that let’s you declare a function in an expression that immediately executes that function? Many JavaScripts common patterns are built around that feature called Immediately Invoked Function Expression best known by its acronym IIFE
// not very useful but clear
(function(){
alert(`I'm a teapot`);
}())
Pure Functions
Hey function, you shouldn’t change anything, you should only accept data and return new data, that’s it.
Behind that simplicity there’s a whole paradigm called functional programming. Let’s take a look a those concepts.
let a = [1, 2, 3, 4, 5];
let b = a.map( i => i+1);
console.log(a); // [1, 2, 3, 4, 5]
console.log(b); // [2, 3, 4, 5, 6]
Side Effects
Hey function you can explicitly (because I told you so) or implicitly (because someone told you so, even without knowing they told you so) change everything you can get your claws into, even without letting anyone know about that.
Scary huh? But also powerful, it all depends on the agreement you have with the rest of the engineers (scary huh ? :P). That said, even here a whole world of paradigms lay on top of that Side Effects thing, like Imperative programming
// a compendium or WRONG STAFF
var a = [1, 2, 3, 4, 5];
var b = [];
a.forEach((v,i) => {
b[i] = ++a[i];
});
console.log(b); // [2, 3, 4, 5, 6] NICE!!!
console.log(a); // [2, 3, 4, 5, 6] OOPS! we change it!!!
Execution Context
See Execution Context documentation
👎 Execution context != context (aka
this
)
👍 Execution context ~ scope
Types of Execution Context (executable code)
Execution Stack
- Starts with global
- Usually LIFO but not necessarily
Evaluation of code by the running execution context may be suspended at various points defined within this specification. Once the running execution context has been suspended a different execution context may become the running execution context and commence evaluating its code. At some later time a suspended execution context may again become the running execution context and continue evaluating its code at the point where it had previously been suspended. Transition of the running execution context status among execution contexts usually occurs in stack-like last-in/first-out manner. However, some ECMAScript features (ES6) require non-LIFO transitions of the running execution context.
Anyone in the audience wants to bring an example of non-LIFO activation record transition feature?
How Execution Context is defined?
Creation Phase
- LexicalEnvironment component creation.
Identifies the Lexical Environment used to resolve identifier references made by code within this execution context.
- Environment Record
-
Declarative Environment Records
Each declarative Environment Record is associated with an ECMAScript program scope containing variable, constant, let, class, module, import, and/or function declarations. A declarative Environment Record binds the set of identifiers defined by the declarations contained within its scope.
Variable
declarationlet
,const
let and const declaration
Function
declarationarguments
-
Each object Environment Record is associated with an object called its binding object. An object Environment Record binds the set of string identifier names that directly correspond to the property names of its binding object. Property keys that are not strings in the form of an IdentifierName are not included in the set of bound identifiers. Both own and inherited properties are included in the set regardless of the setting of their
[[Enumerable]]
attribute. Because properties can be dynamically added and deleted from objects, the set of identifiers bound by an object Environment Record may potentially change as a side-effect of any operation that adds or deletes properties. Any bindings that are created as a result of such a side-effect are considered to be a mutable binding even if the Writable attribute of the corresponding property has the value false. Immutable bindings do not exist for object Environment Records.
-
- Reference to the outer environment,
-
this
binding.The value associated with the
this
keyword within ECMAScript code associated withthis
execution context.
- Environment Record
-
VariableEnvironment
component creation.Identifies the Lexical Environment whose environment record holds bindings created by VariableStatements and FunctionDeclarations within this execution context.
var
var declaration
Execution Phase
example taken from: Understanding Execution Context and Execution Stack in Javascript. (by Sukhjinder Arora)
0 - The code
let a = 20;
const b = 30;
var c;
function multiply(e, f) {
var g = 20;
return e * f * g;
}
c = multiply(20, 30);
1 - Global context creation
// (PSEUDO-CODE)
GlobalExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// Identifier bindings go here
a: < uninitialized >,
b: < uninitialized >,
multiply: < func >
}
outer: <null>,
ThisBinding: <Global Object>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Object",
// Identifier bindings go here
c: undefined,
}
outer: <null>,
ThisBinding: <Global Object>
}
}
2 - Global execution
// (PSEUDO-CODE)
GlobalExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Object",
// Identifier bindings go here
a: 20,
b: 30,
multiply: < func >
}
outer: <null>,
ThisBinding: <Global Object>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Object",
// Identifier bindings go here
c: undefined,
}
outer: <null>,
ThisBinding: <Global Object>
}
}
3 - multiply
context creation
// (PSEUDO-CODE)
FunctionExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Identifier bindings go here
Arguments: {0: 20, 1: 30, length: 2},
},
outer: <GlobalLexicalEnvironment>,
ThisBinding: <Global Object or undefined>,
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Identifier bindings go here
g: undefined
},
outer: <GlobalLexicalEnvironment>,
ThisBinding: <Global Object or undefined>
}
}
4 - multiply
execution
// (PSEUDO-CODE)
FunctionExectionContext = {
LexicalEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Identifier bindings go here
Arguments: {0: 20, 1: 30, length: 2},
},
outer: <GlobalLexicalEnvironment>,
ThisBinding: <Global Object or undefined>,
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// Identifier bindings go here
g: 20
},
outer: <GlobalLexicalEnvironment>,
ThisBinding: <Global Object or undefined>
}
}
Articles and books used for the “execution context” section
- Standard ECMA-262
- Harold Abelson and Gerald Jay Sussman with Julie Sussman
- Dmitry Soshnikov
- Michael McMillan
- Sukhjinder Arora
- Rupesh Mishra
- Bilal Alam
Scope
REMINDER
DO NOT CONFUSE “scope” with “context”
Define Scope
Scope is the “part of the program” where a Name Binding is valid.
“Part of a program” meaning
WHEN defined at RUN-TIME ( Dynamic Scope )
“Dynamic scoping does not care how the code is written, but instead how it executes. Each time a new function is executed, a new scope is pushed onto the stack. This scope is typically stored with the function’s call stack. When a variable is referenced in the function, the scope in each call stack is checked to see if it provides the value.”
Source: Lua
Late binding, non-optimizable at compile time
WHEN defined at LEX-TIME ( Lexical Scope )
“The scope of a quantity is the set of statements and expressions in which the declaration of the identifier associated with that quantity is valid.”
Source: Algol60
Early binding, optimizable at compile time
JavaScript use this scope paradigm
ECMAScript definitions
General definitions:
Examples
/**
* REMEMBER
* In JavaScript a variable can't have a reference to another variable!!!! IT'S ALL ABOUT VALUES!!!!
*
* A value can be assigned to a variable in two ways
* BY VALUE -> for primitives -> the data is duplicated and then the variable points to that new duplicated data
* var a = 2;
* var b = a; -> a new numeric data is created and b will point to this new data
* there's no relationship between the 2 of a and the 2 of b
*
* BY REFERENCE -> for compound (objects) -> the variable points the the data
* var a = {x: 3};
* var b = a; -> there's no reference from b to a,
* the references if from that both a and b point to the same object in memory { x:3 }
*
* It means if we modify the actual object { x:3 } by adding or removing information, all identifiers will
* be still linked to the same data which has changed. Instead of we reassign the identifier data or redefine the identifier,
* the data will be accessible through any remaining identifier pointing to it otherwise it'll be garbage collected
*
* Below a simple verification
*/
// Synchronous modification
var a = { x:3 };
var b = a;
a = { n:8 };
console.log(a); // -> {n: 8}
console.log(b); // -> {x: 3}
// Asynchronous modification
var c = { x:3 };
var d = c;
// will see asynchronous programming and arrow functions later
setTimeout(() => c = { x:4 }, 1000);
setTimeout(() => console.log(c), 2000); // -> {x: 4}
setTimeout(() => console.log(d), 3000); // -> {x: 3}
/**
* explain scope of:
* a
* b
* c
* d
*
* what is TDZ in ES6?
*/
function n ( z ) {
a = 3;
var b = 4;
{
var c = 5;
let d = 7;
}
}
/**
*
*/
(function(){
var a = b = 3;
})();
console.log(a); // what will it print?
console.log(b); // and this?
Hoisting
Hoisting is a concept described by Douglas Crockford many years ago, way before ES6 was even close to be dreamed. That concept was particularly significant in ECMAScript < 5.x and ES6 introduced a couple of changes in the syntax that directly affects how that concept is defined. Kyle Simpson dedicated a full section for that and, truly, there’s no better explanation I can share with you than that.
Let’s see YDKJS - Scope & Closures - Hoisting
Closure
“A closure is the combination of a function and the lexical environment within which that function was declared.”
Source: MDN
“In programming languages, a closure, also lexical closure or function closure, is a technique for implementing lexically scoped name binding in a language with first-class functions. Operationally, a closure is a record storing a function together with an environment.The environment is a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created. Unlike a plain function, a closure allows the function to access those captured variables through the closure’s copies of their values or references, even when the function is invoked outside their scope.”
Source: Wikipedia
“In computer science, a closure (also lexical closure, function closure, function value or functional value) is a function together with a referencing environment for the non-local variables of that function. A closure allows a function to access variables outside its typical scope. Such a function is said to be “closed over” its free variables. The referencing environment binds the non-local names to the corresponding variables in scope at the time the closure is created, additionally extending their lifetime to at least as long as the lifetime of the closure itself. When the closure is entered at a later time, possibly from a different scope, the function is executed with its non-local variables referring to the ones captured by the closure.”
Source: Enacademic
General Definition
/**
* CLOSURE example from wikipedia
* @see https://en.wikipedia.org/wiki/Closure_(computer_programming)#Lexical_environment
*
* in ECMAScript
*/
var f, g;
function foo() {
var x;
f = function() { return ++x; };
g = function() { return --x; };
x = 1;
alert('inside foo, call to f(): ' + f());
}
foo(); // 2
alert('call to g(): ' + g()); // 1 (--x)
alert('call to g(): ' + g()); // 0 (--x)
alert('call to f(): ' + f()); // 1 (++x)
alert('call to f(): ' + f()); // 2 (++x)
/**
* Function foo and the closures referred to by variables f and g all use the same relative memory location signified by local variable x.
*
* In some instances the above behavior may be undesirable, and it is necessary to bind a different lexical closure.
* Again in ECMAScript, this would be done using the Function.bind(). We'll see `bind` later.
*/
Relative Concepts Readings
- Name Binding
- Identifier
- Local Scoped Variable
- Global Scoped Variable
- Block Scoped Variable
- First Class Citizen
- Higher order function
- Garbage collection
- Free variables and bound variables
- Domain of discourse
- Referential transparency
- Side effect
- Function pointer
Can we cheat scope?
“Abandon all hope who enter here”
Ev[a|i]l
Modifying the lex-time defined scope dynamically
/**
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
*
*/
function cheat(a, b, c) {
// 'use strict'; // <- what if we use strict mode? ( we'll see strict mode details on day 7 )
eval(a);
console.log(b + c);
}
cheat('var c = 7', 10, 5); // <- what if we omit the var statement?
function cheatAgain(a, b) {
// 'use strict'; // <- what if we use strict mode? ( we'll see strict mode details on day 7 )
eval(a);
console.log(b + c);
}
var c = 5;
cheatAgain('var c = 7', 10); // <- what if we omit the var statement?
with
Creating a lexical scope at run-time
/**
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with
*
*/
var a = {
b: 2
};
var a1 = {
c: 3
}
with(a){
b = 3;
}
with(a1){
b = 4;
}
console.log(a);
console.log(a1); // <- oops
console.log(b); // <- what?!!!
'use strict';
with (Math){x = cos(2)};
Other
- A great analysis of a memory leak example related to inaccurate use of closures See here
- The best and more detailed book I’ve ever read about scope and closures in JS You Don’t Know JS: Scope & Closures
Preliminary Practice
Now let’s have some time to practice. Here a list of resources we can use:
Exercises
Let’s open our test files:
Now open your terminal.
- Make sure you’re at the project location
- If you didn’t install all the packages yet the run
npm i
for a fresh dependency install, ornpm ci
for an installation based on the lock file. - Type
npm run test:watch
, this will start running your tests every time you make a change.
Our task is to make ALL our DAY 6 tests pass ;)