Advanced Reminders
Specific iteration control keywords
There is two specific keywords used to control how the iteration goes:
break
continue
The break
statement is used to terminate a loop, switch, or in conjunction with a labeled statement (more info here). When you use break
without a label -- the most probable way of using it -- it terminates the innermost enclosing while
, do-while
, for
, or switch
immediately. The important thing to remember is that it transfers control to the following statement.
This statement is quite interesting because it prevents unnecessary iteration over the entities being iterated, in addition to being clear.
The continue
statement can be used to restart a while
, do-while
, for
, or label statement. When you use continue
without a label, it terminates the current iteration of the innermost enclosing while
, do-while
, or for
statement and continues execution of the loop with the next iteration. In contrast to the break statement, continue
does not terminate the execution of the loop entirely. In a while
loop, it jumps back to the condition. In a for
loop, it jumps to the increment-expression.
Special loops
In addition to these three loops, there also exists three more loops mostly used to iterate over element of a collection:
forEach
for... in
for... of
forEach
is not quite a special control structure but more a prototype method attached to the Array
object (more information about prototype in the OOP section). It executes a provided function once for each array element. Be careful however, since this method does not copy the array: it directly uses the collection and if it cause some desynchronisation or unexpected behavior, mostly if you remove/add new element in the collection.
The =>
element indicates an arrow function. We will talk about that below, and in the Errors & Promises part.
The for...in
statement iterates a specified variable over all the enumerable properties of an object. For each distinct property, JavaScript executes the specified statements. A for...in
statement looks as follows:
Use the for...in
statement wisely! For example, it is not advise to use it for Arrays
instead of for
. The principal reason is that it will iterate also on the user-specified variable, not only on the numerical index.
The for...of
statement creates a loop iterating over iterable objects (including Array
, Map
, arguments object and so on), invoking a custom iteration hook with statements to be executed for the value of each distinct property. A for...of
statement looks as follows:
A for...in
loop would have displayed 0,1,2,"someText"
instead of the value of the array.
A hook is a special invokation function mecanism. More information below.
Iterator and generator
Iterator
The notion of iterator comes from the fact that processing each item of a collection is a very common operation in computing. The important notions behind an iterator are that at a specific moment it points toward a specific item of the collection and that there is a well defined sequence in the collection (implied by the specific moment). Thus, an iterator must answer the following questions:
In JavaScript an iterator is an object which defines a sequence and potentially a return value upon its termination (simply stated, this is the iteration protocols):
Is there any element left in the collection ?
What is this very element if it exists ?
In fact, you already have used an iterator by using loop such as for...of
without knowing it.
Technically speaking, any object is qualifiable as an iterator if it has a next()
method and it returns an object with the two following properties:
done
: a boolean value indicating whether or not there are any more items that could be iterated upon. Iftrue
, there is no more item.value
: the current element
If you have a custom type and want to make it iterable so that you can use the for...of
loop construct, you need to implement the iteration protocols. The following code creates a Sequence
object that returns a list of numbers in the range of (start
, end
) with an interval between subsequent numbers.
To use the built-in iterator of Sequence
, there is mainly two ways:
❓ A well though iterator can be very efficient and versatile. For example, for your Mahjong project, you could define an iterator iterating over a familly, and making it also generate this very familly (somewhat like Sequence
).
Generator & Iterable
One issue with custome iterators is that their creation requires careful programming since their internal state has to be explicitly maintained. To circumvent this issue, generator functions allow the definition of a single function whose execution is not continuous.
When called, generator functions do not initially execute their code. Instead, they return a special type of iterator, called a Generator. When a value is consumed by calling the generator's next method, the Generator function executes until it encounters the yield
keyword. In other hand, the execution is suspended once a yield
is encountered, and resumed after it at the next call.
The function can be called as many times as desired, and returns a new Generator each time.
Generator functions are written using the function*
syntax, as follow (taking the Sequence
example above, droping the class aspect):
Generators compute their yielded values on demand, which allows them to efficiently represent sequences that are expensive to compute (or even infinite sequences, as demonstrated above). An advanced generator could be, for the fibonacci sequence:
To conclude, an object is directly iterable if it has an iteration behavior by implementing the @@iterator
method. This simply means that the object needs to have a property [Symbol.iterator]
.
To make your own iterables:
In the difference of generators where their iterator can be iterated only once, an iterable can be used several times.
Functions
Anonymous functions
There is a difference between a function delcaration and a function expression. A function The later can have its name omitted, defining an anonymous function. The main difference between a function expression and a function declaration is the function name, which can be omitted in function expressions to create anonymous functions.
An anonymous function will do nothing on its own (except enclosed in a grouping operator -- see beelow). It need to be "manually" invoked.
These kind of functions are commonly used along event handler or callback function.
However, anonymous functions are not condamned to be used only once. In fact, a function expression can be assign to a variable, making it invokable wherever needed.
Stored anonymous function can be a good way to separate your functions -- which actually do things -- from your event handlers -- which triggers things. Example:
IIFE (Immediately Invoked Function Expression)
A function expression can be used as an IIFE (Immediately Invoked Function Expression) which runs as soon as it is defined. In addition of being directly interpreted, an IIFE has its lexical scope enclosed within the Grouping Operator ()
. This prevents accessing variables within the IIFE idiom as well as polluting the global scope.
An IIFE cannot be stored in a variable. Assigning it to a variable stores its return value instead of its definition. An IIFE
statement looks as foolow:
Arrow function
An arrow function expression is a compact alternative to a traditional function expression, but is limited and cannot be used in all situations. One of the major reason arrow functions were introduced was to alleviate scope and context complexities ( this
) thus making functions execution much more intuitive. There is several way of declaring an arrow function. The two most commons are:
Check MDN for more in depth information about arrow functions.
Arrow function are best suited for non-method functions, mostly due by the scope (non-)modification implied. By using it as an object method, since an object does not create a new scope the this
context does not change, and the arrow-function does not have its own this
. It can be a good candidate for promises.
Scope and context
Scope
In JavaScript, there is two type of scopes:
Local: Variables declared inside a function is in the local scope of this very function. Therefore, each function creates a new scope when defined.
Global: Variables declared outisde of a function is in the global scope. Variables inside the Global scope can be accessed and altered in any other scope.
Conditionnal statements (e.g. if
, switch
) and loops, unlike functions, don't create a new scope. Variables defined inside of them remains in the scope they were already in while declared.
HOWEVER! let
and const
statement for variable declaration support the declaration of local scope in conditionnal and loop statements. This means that at the end of the statement, all the variables declared with either let
or const
is destroyed.
Global scope lives as long as your application lives. Local Scope lives as long as your functions are called and executed.
Context
Context refers to the value of this
(the introspection operator) in some particular part of the scope of your code. In the global scope context is always the Window object (this === Window
).
Programer tend to often confuse scope for context, and vice et versa. But they are not the same concept! Scope refers to the visibility of variables in a specific code location ; context, their values in a specific scope.
When declaring a new object -- or using the new
operator, the context changed.
Which mean that normal function and arrow function does not share the same behaviour.
Lexical Scope and Closure
Lexical Scope means that in a nested group of functions, the inner functions have access to the variables and other resources of their parent scope. This means that the child functions are lexically bound to the execution context of their parents. Lexical scope is sometimes also referred to as Static Scope.
However, the lexical scope only works forward. That means that a parent scope can not have access to a children's scope.
Closure is an advanced technic of scopes managment and variables lifetime. A closure can not only access the variables defined in its outer function but also the arguments of the scope chain of its outer function (i.e. the variables outside of the immediate lexical scope). Closures contain their own scope chain, the scope chain of their parents and the global scope.
To create a closure, the idea is to return a function from a function. By doing so, variables from the lexical scopre are not deallocated (check execution context for more information), and remain accessible from this function.
This is an interesting behaviour. More examples here.
Hoisting
Variable hoisting is a mecanism in JavaScript implied by how the code is processed before its interpretation. This has for effect to allow post-variable declaration in the code, even if the variable is used beforehand.
There is no function hoisting! Function must be declared before they can be used.
Be careful however, since hoisting can lead to leaking variable. For example
Last updated
Was this helpful?