Chapter 6: Advanced working with functions
6.1 Recursion and Stack
If you are familiar with programming, you may skip this chapter.
6.2 Rest parameters and spread syntax
Rest parameters ...
The rest parameters must be at the end.
The "arguments" variable
There is also a special array-like and iterable object named arguments that contains all arguments by their index.
Arrow functions do not have "arguments".
Spread syntax
...arr
"expands" an iterable object arr into the list of arguments.
The spread syntax works only with iterables. It internally uses iterators to gather elements, the same way as for..of does.
Get a new copy of an array/object
6.3 Variable scope, closure
Code blocks
If a variable is declared inside a code block {...}
, it’s only visible inside that block.
For if, for, while and so on, variables declared in {...}
are only visible inside.
Nested functions
A function is called “nested” when it is created inside another function. It can be returned by the outer function.
Lexical Environment
Step 1. Variables
Every running function, code block {...}
, and the script as a whole have an internal associated object known as the Lexical Environment.
Environment Record – an object that stores all local variables as its properties. A “variable” is just a property of the special internal object, Environment Record.
A reference to the outer lexical environment, the one associated with the outer code.
When the script starts, the Lexical Environment is pre-populated with all declared variables.
Then
let phrase
definition changes the value toundefined
.phrase
is assigned a value.phrase
changes the value.
Step 2. Function Declarations
A Function Declaration is instantly fully initialized. That’s why we can use a function even before the declaration itself. (This behavior only applies to Function Declarations.)
Step 3. Inner and outer Lexical Environment
During the function call we have two Lexical Environments: the inner one (for the function call) and the outer one (global). The inner Lexical Environment has a reference to the outer one.
When the code wants to access a variable, the inner Lexical Environment is searched first, then the outer one, then the more outer one, and so on until the global one.
Step 4. Returning a function
All functions remember the Lexical Environment in which they were made. The [[Environment]]
reference is set once and forever at function creation time.
When the code inside counter()
looks for count variable, it first searches its own Lexical Environment (empty, as there are no local variables there), then the Lexical Environment of the outer makeCounter()
call, where it finds and changes it.
A variable is updated in the Lexical Environment where it lives.
A closure is a function that remembers its outer variables and can access them. In JavaScript, they automatically remember where they were created using a hidden [[Environment]]
property, and then their code can access outer variables.
Garbage collection
If there’s a nested function that is still reachable after the end of a function, then it has [[Environment]]
property that references the outer lexical environment. A Lexical Environment object dies when it becomes unreachable.
Real-life optimizations
In theory while a function is alive, all outer variables are also retained. An important side effect in V8 (Chrome, Opera) is that such variable will become unavailable in debugging.
They analyze variable usage and if it’s obvious from the code that an outer variable is not used, remove it.
6.4 The old "var"
"var has no block scope
Variables, declared with var, are either function-wide or global. They are visible through blocks.
If a code block is inside a function, then var becomes a function-level variable.
“var” tolerates redeclarations
“var” variables can be declared below their use
var
declarations are processed when the function starts (or script starts for globals).
Declarations are hoisted, but assignments are not. The assignment always works at the place where it appears
IIFE
Immediately-invoked function expressions: programmers invented a way to emulate the block-level visibility for var
.
6.5 Global object
The global object provides variables and functions that are available anywhere. By default, those that are built into the language or the environment.
window
: Browswerglobal
: Node.jsglobalThis
: Standard name for a global object (Recent change)
In a browser, global functions and variables declared with var
(not let
/const
!) become the property of the global object.
Using for polyfills
We use the global object to test for support of modern language features.
For instance, test if a built-in Promise object exists (it doesn’t in really old browsers):
6.6 Function object, NFE
The "name" property
Function objects contain some useable properties.
A function’s name is accessible as the name
property. It also assigns the correct name to a function even if it’s created without one.
The “length” property
length
property returns the number of function parameters.
Custom properties
A property assigned to a function does not define a local variable counter inside it. It can replace closure, but the property can be modified by outer codes.
Named Function Expression
It allows the function to reference itself internally.
It is not visible outside of the function.
6.7 The "new Function" syntax
Syntax
The function is created literally from a string, that is passed at run time. For example, we can receive a new function from a server and then execute it, or dynamically compile a function from a template, in complex web-applications.
Closure
When a function is created using new Function, its [[Environment]]
is set to reference not the current Lexical Environment, but the global one.
If new Function
had access to outer variables, it would have problems with minifiers because outer varaibles are renamed into shorter ones.
6.8 Scheduling: setTimeout and setInterval
setTimeout and clearTimeout
setInterval
The real delay between func calls for setInterval
is less than in the code, because the time taken by func's execution "consumes" a part of or even higher than the interval.
The nested setTimeout
guarantees the fixed delay.
Zero delay setTimeout
The scheduler will invoke the function only after the currently executing script is complete.
In browser, after five nested timers, the interval is forced to be at least 4 milliseconds.
6.9 Decorators and forwarding, call/apply
Transparent caching
The result of cachingDecorator(func)
is a “wrapper”: function(x)
that “wraps” the call of func(x)
into caching logic.
func.call
func.call()
runs func providing the first argument as this, and the next as the arguments.
func.apply
call
accepts an iterable (...)apply
accepts an array-like object
Borrowing a method
To let any iterable and array-like to use the join
method:
Technically the join
method takes this
and joins this[0]
, this[1]
...etc together.
6.10 Function binding
Losing “this”
That’s because setTimeout
got the function user.sayHi
, separately from the object.
Solution 1: a wrapper
Vulnerability: Before setTimeout
triggers, user
changes value. Thus it will call a wrong object.
Solution 2: bind
Calling boundFunc
is like func
with fixed this
.
We take the method user.sayHi
and bind it to user
.
Partial functions
We can bind this
and arguments to a function.
Use bind
to create a function double
on mul
:
For instance, we have a function send(from, to, text)
. Inside a user object we may want a partial function sendTo(to, text)
that sends from the current user.
Going partial without context
If we’d like to fix some arguments, but not the context this
, use this function instead of the native bind
:
6.11 Arrow functions revisited
Have no
this
. Ifthis
is accessed, it is taken from the outside.Have no
arguments
.Have no
super
.Can’t run with
new
.
Last updated