Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
JavaScript is a scripting or programming language that allows you to implement complex features on web pages. It enriches both HTML and CSS languages by allowing complex operations of their entities, mostly HTML ones. JavaScript enables you to create dynamically updating content, control multimedia, animate images, react to events in the page and pretty much everything else. It is the third layer of the standard web technologies.
One of the great strength of the client-side JavaScript is the functionnality built on top of it. So-called API (standing for Application Programming Interfaces), they provide extra and important features, ready to use. They generally fall into two categories:
Brower APIs
Third Party APIs
Browser APIs are built into your web browser, and are able to do useful complex operations as well as using data from the computer environment. Examples are:
DOM (Document Object Model) API
XmlHttpRequest (XHR) API
Canvas et WebGL APIs
...
Third party APIs are APIs not built directly in the browser. Instead, you rely on other provider to give acces to the code somewhere on the web.
Twitter API
OpenStreetMap API
On an security note, each browser tab creates its own separate execution environment, generally preventing that a code can affect a code runing in another tab.
On a performance note, JavaScript is an interpreted non-typed language, which means it is not compilled into another form and that variable does not have a type. This has the main advantage of being easly readable and easily modifiable on the flight. However, this hinders somewhat its performance compared to other compiled language. Nevertheless, from a technical standpoint, most modern JavaScript interpreters actually use a technique called just-in-time compiling to improve performance; the JavaScript source code gets compiled into a faster, binary format while the script is being used.
JavaScript interprets a lot of things and allows some ambigus semantic, thus encouraging bad practices and habits, which can quickly transfer to other languages (e.g. anonymous function in C++? wtf). You must be careful when coding and well designing your code, otherwise this will quickly run into a coding hell!
For example, the variable hoisting concept:
Other example with arrays manipulation:
Oh, you also have an auto semi-colon adder, which means that var a = 7;
is equal to var a = 7
...
For fun, go check this video and start the video at 1:22
, or even this one.
To insert some javascript to your page, there is only one way: by using the <script>
element. Generally speaking, this element is placed in between the <head>
element of your page. One script
element is used for one javascript script, so if you have several of them, the same goes for the elements.
Note that even if you can put javascript code directly inside a <script>
element, this is strongly discouraged. Instead, correctly organise your scripts.
There are a number of issues involved with getting scripts to load at the right time. A common problem is that all the HTML on a page is loaded in the order in which it appears in your code (thus, from top to bottom). If you are using JavaScript to manipulate elements on the page (or more accurately, the Document Object Model -- or DOM), your code won't work if the JavaScript is loaded and parsed before the HTML you are trying to do something to.
Generally speaking, this will generate errors when your JavaScript will be interpreted by the web browser. For example, associating an event lister to a specific HTML element which does not yet exist will return an error. Thankfuly, there is some strategies to circumvent this problem.
Here, we can see the loading strategies of the page: first load the html, then load all the other resources.
The old way consists to listen the main document of your page and catch the event related to the HTML body being completely loaded and parser. The event is DOMContentLoaded
. Consequently, the content inside such an event listener will not run until after that event is fired (then catch): this prevent the error to happens. However, this is rather a disgracious method.
There are actually two modern features we can use to bypass the problem of the blocking script:
async
defer
Scripts loaded using the async
attribute will download the script without blocking rendering the page and will execute it as soon as the script finishes downloading. You get no guarantee that scripts will run in any specific order, only that they will not stop the rest of the page from displaying. It is best to use async when the scripts in the page run independently from each other and depend on no other script on the page.
async
should be used when you have a bunch of background scripts to load in, and you just want to get them in place as soon as possible.
Inversely, scripts loaded using the defer
attribute will run in the order they appear in the page and execute them as soon as the script and content are downloaded.
Thus, if the script taskboard_pool.js
relies on board_global_config.js
(for example, the later creates useful data structures for the other type of boards), then you should use defer
for the loading.
Nowaday, recent web browsers, especially Firefox and Chromium based web browser can be considered as small IDEs. They implement strong features, easy to use, such as:
An HTML inspector
A console
A debugger
A network observer
A profiler
The debuggers are especially not to be underevaluated. They are really strong, and since JS is an interpreted language, they can modify the context of any bloc, variables and functions on the flight.
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.
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.
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. If true
, 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
).
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.
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:
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:
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.
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 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 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.
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
JavaScript is a weakly typed language, mostly used to manipulate the DOM of a web page.
Additionally, JavaScript is an object-oriented language (but it allows imperatif and also lambda calculus). However, its main difference with language like Java or C++ is that it is a prototype based language and not a class based one. We will saw the implication of such a statement in the Object-oriented programming section. Briefly said, anything but primitive types is an object in JavaScript.
This section serves as a quick reminder, mostly regarding the syntax, for several important notions.
JavaScript has three different conditionals, well known in other languages as well.
The if...else
conditional.
The ternary statement.
The switch
statement.
JavaScript has three different ways to perform an egality check, either with their own specificity.
The first one is the simple equality made by the ==
operator. It does make a data type converstion before the comparison of the objects. You can have important side effects if not used correctly.
The second equality operator is the strict operator ===
(3 =
). The most notable difference between this operator and the simple equality operator is that if the operands are of different types, it will not attempt to convert them to the same type before comparing.
The last equality operator is Object.is()
operator. It determines whether two values are the same value. It is widely different to ==
since it does not perform coerce checks, and differ from ===
in regard that it treats the number values -0
and +0
as equal and treats Number.NaN
as not equal to NaN
.
In JavaScript, the 3 classic loop structures are available:
for
while
do...while
The for
loop repeats until a specified condition evaluates to false. A for
statement looks as follow:
The do...while
statement repeats until a specified condition evaluates to false. It looks as follows (do not forget the semi colon at the end of the while instruction):
A while
statement executes its statements as long as a specified condition evaluates to true. It looks as follows:
JavaScript is weakly typed, meaning that the variables do not have a specific on their declaration. Instead, we use a generic keyword declaration to declare a new variable.
There is only three declaration keywords in JS. They are:
let
: allows the declaration of a block scope local variable (it is destroyed while outside the block) ; it can optionally be initialized to a value ;
var
: declares a variable, optionally initializing it to a value ;
const
: declares a read-only named constant.
Basically, a function is defined by its name, and its parameters. It does not have a return type: therefore you must be carreful about how the code is documented. A function declaration looks as follow:
To invoke a function:
JavaScript has been made to handle client side application, and therefore has been designed with this objective in mind. One of the principal particularity of client side is the presence of a user, needing a highly interactive interface for browsing. Consequently, JavaScript event managment has quickly become one of its strength.
An event can be seen as a signal that something has happened. All nodes from the DOM generate such signals. Below are some useful DOM events:
Mouse events
click
– when the mouse clicks on an element (touchscreen devices generate it on a tap).
contextmenu
– when the mouse right-clicks on an element.
mouseover
/ mouseout – when the mouse cursor comes over / leaves an element.
Form element events
submit
– when the visitor submits a <form>
.
focus
– when the visitor focuses on an element in the form, e.g. on an <input>
.
Document events
DOMContentLoaded
– when the HTML is loaded and processed, DOM is fully built.
CSS events
transitionend
- when a CSS-animation finishes.
When such actions are performed (e.g. a button is clicked), we say that the click event is fired (or dispatched).
To react on events (i.e. catch a dispatched event) we can assign a handler – a function that runs in case of an event.
There is several ways to assign handlers to HTML elements. We will only see the more flexible and less error prone here: addEventListener
(doc. here). As an example, lets say we have an HTML input button
To handle the click
event, we first need to retrieve our HTML element (here the input) in our JavaScript code, then attach to it our handler.
Multiple call to addEventListener
for a same HTML element will add the handlers. This means that you can stack several functions for one event!
When an event is dispatched, it is often in a specific context which is important to us in order to understand what was going on. For example, in the case of a mouse click, where was the coordinate of the mouse?
Usually, when an event happens, the browser create what it is called an event object. This object contains the details of the event and is passed as an argument to the handler.
It is also possible to attach to a handler not a function but an object! The only prerequisite is that the object implements a handleEvent()
method. The method also receives the event object.
We can also use a class to better organize our code, especially if we want to delegate the events. In the following example we listen the up and down of the mouse with a single object, and the event is then dispatched.
JavaScript allows you to create and manually manage your own events, which prevent you to fully reimplement an event handler. They are called synthetic events, as opposed to the events fired by the browser itself.
These events are DOM events: they rely on the DOM API of your browser, and are considered as event targets (cf. EventTarget()
). They are not supposed to be events produced by your objects.
To create a new event, the Event
constructor can be call
Then, this new type of event can be attached to any DOM elements, and listened to.
And to fire the event
You can also add custom data to the event by using the CustomEvent
constructor
However, you will mostly rely on already existing events, such as 'click'
, 'drag'
, etc. The full list is accessible here.
The principle of bubbling events is the following:
When an event happens on an element, it first runs the handlers on it, then on its parent, then all the way up on other ancestors.
When, lets say, a click is made on <p>
, the onclick
event is run firstly on <p>
, then <div>
, then <form>
. The process is called “bubbling”, because events “bubble” from the inner element up through parents like literally an air bubble in the water.
An interesting point is that an event handler on a parent element can always get the details about where the event actually happened.
The event.target
property stores the most deeply nested element that cause the event, while event.currentTarget
stores the element where the handler is (this
, in the example below the form).
event.target
– the deepest element that originated the event.
event.currentTarget
(=this) – the current element that handles the event (the one that has the handler on it)
event.eventPhase
– the current phase (capturing=1, target=2, bubbling=3).
All these previous notion, and mostly bubbling, allow us to implement powerful event handling patterns.
The idea behind the event delegation pattern, is that, when several elements on an HTML page are supposed to be handled in a similar fashion, we define a single handler on their common ancestor in charge of all these elements.
One can say that web component approach emphasize this pattern by making a specific type of component self-aware of its events. Check the single page application topic for more information.
This is made possible because we have the event object with the target
property, alowing us to see where the event actually took place in the page.
For example, imagine that we have a table, with various element inside each cell. The objective is to overlay the clicked cell.
Delegation pattern can also be used for other uses. For example, to match action in a menu. The trivial solution is to assign for each entry of the menu an handler on the click. But there is a more elegant solution to that using delegation.
For that, we will use HTML data
attribute, attach the handler directly to the menu and rely on a menu object. All the logical operation will be performed directly in the object, which is a good way to structure your code.
HTML:
JS
Please note that this.onClick
is bound to this
in (1). That’s important, because otherwise this
inside onClick
would reference the DOM element (elem), not the Menu object, and this[action]
would not be what we need (cf. Scope and Context).
The behavior pattern rely both the delegation pattern and the data
attribute of HTML. The idea behind this pattern is to assigne specific action to specific data-attribute, by using a top level delegation. In other words, we attach to the document
itself a handler and verify the very existence of the attribute. It has thus two parts:
We add a custom attribute to an element that describes its behavior.
A document-wide handler tracks events, and if an event happens on an attributed element – performs the action.
Example of counter associate with data-counter
:
You can use this approach for toggling visibility and elements on the page.
Anything that is not a primitive type (undefined
, null
, number
, string
, boolean
, bigInt
, symbol
) is an object (or an instance) in JavaScript. An object is used to store keyed collections of various data and more complex entities. Object instances can contain more instances which can be functions. These specific functions belongig to an object are called method
. That also means functions are objects.
An object can be created with the object literal syntax {...}
. The element stored inside an object are called property
, and we will see how add and remove them from an object. An object can also be created by using the new
operator.
We can create our object with predefined properties as key: value
pairs:
It is possible to also put a coma ,
at the end of the last line. This is known as trailing: since all the lines are alike, it makes it easier to add/move/remove properties.
The value
part can also be a variable!
Property values can be accessed using the dot notation:
Remember that variable does not have a type! Thus, we could have done quickdraw.weight="HEAVY!"
with no error at all.
A property can also be accessed by using the square brackets notation []
. It is more powerful that dot notation, but also more cumbersome to write. To access a property using square brackets notation:
Square bracket also permits to use variable as key
value, which is very convenient.
A property can be added to an object "on the fly", after it has been created.
To delete a property, use the delete
keyword.
Square bracket also permits to make computed property, using variable to create new property for an object. It can be done on the fly or while the creation of the object with the literal syntax {...}
.
A const
object's properties can still be changed! For example, suppose our const quickdraw={weight: 15}
object. We can change its weight with no error. But we cannot change the value of the variable quickdraw
itself (e.g. replacing it by another object).
JavaScript strength is its introspection almost limitless possibilities. Consequently, it is possible to access any property of an object, and plus, there will be no error if the property doesn’t exist. Indeed, a non existing property will just return an undefined
value. There is also the in
operator (much more elegant than the ===
operator) to test the existence of such properties.
Remember the for...in
loop iterating over the properties of an object? No? Go review the Advanced Section.
Property can be more than just a primitive value. In fact, they can store other objects, or even functions.
In JavaScript, objects are stored and copied by reference, as opposite to primitive type. That means a variable storing an object always have its reference as value, and the object is stored somewhere in the memory. JavaScript performs auto-dereferencing of the address stored in the variable to get the actual object (and perform the desired action).
This is important because when an object variable is copied, it is not the object that is copied, but it is the reference! The object is not duplicated.
These two variables references the same object. Making a property modification of one of the variable will instantetly reflect on the other as well! Below an exemple to help you grasp the concept.
This mean that it is not trivial to entierely copy an object which is independent of the first. This is called cloning. In fact, JavaScript does not has built-in function for that, and you will see that it is rarely needed.
But if we really want that, then we need to create a new object and replicate the structure of the existing one by iterating over its properties and copying them.
This copy only the primitive level of the object. If you have another level in your object (e.g. another object), and you want to deep copy them, you will need to go this level. There is already some function here and there to do that, you could check the Lodash lib for example.
As we have seen, {...}
operator allows us to create one new object. However, when several similar objects are required, this method is not practicable (e.g tiles of mahjong). Constructor functions and new
operator are what we need to do that.
Constructor function are regular functions, with no difference to a simple function. However, they should respect these two conventions (they are not implemented in a language-level):
The function should start with a capital letter
The function should only be called using the new
operator
The syntax is as follow
When a function is executed with the new
operator, it does the following steps:
A new empty object is created and assigned to this.
The function body executes. Usually it modifies this, adds new properties to it.
The value of this is returned.
There much more to learn on constructor function and new. But we want to emphasize on OOP, thus we will lean toward a more appropriate syntax
With a little imagination and knowledge about the language, it is totally possible to force function's visibility for function constructor. Use the ()
operator, IIFE concept and the concept of closure.
The return statement of the Quickdraw contains our public functions. The private functions are just those that are not returned. Not returning functions makes them inaccessible outside of the Quickdraw namespace. But our public functions can access our private functions thanks to closure.
A convention is to use the _
(underscore) symbol as a prefix for protected method, and returning an anonymous object containing the public functions.
Take some time to fully understand this code.
In object-oriented programming, a class is an extensible program-code-template for creating objects, providing initial values for state (member variables) and implementations of behavior (member functions or methods). (Wikipedia)
A class
is the fondamental element of the object-oriented programming paradigm, and we want that to be convenient to use and powerful. While constructor function and new
operator help us to create many similar objects, a class
construct give us much more features.
The syntax of a class is:
So if you retake our quickdraw example, to define a class and instantiate an object of this class
Even if alert(typeof Quickdraw)
says that a class is a function, it is not just a syntactic sugar. One of the important thing is that in the prototype of the object, the enumerable
flag is set to false
for all methods of the class. That means iterations instructions, like for...in
, will only iterate over the properties of the object, not its methods.
Classes always use strict mode, even if you are in sloppy mode (the default)! All code inside the class construct is automatically in strict mode. This mean that the behavior of somes JavaScript feature can change! Be aware of that, such as this
value and the execution scope. More information here.
Class fields are defined for each instance of the class, not the class itself. For example Quickdraw.prototype.weight
is undefined, whereas myQckd.weight
is set.
JavaScript introduced getters and setters concept both for literal function and class. Easily enough, the syntax is:
Getters and setters are specific functions. When declaring a get/set over a property of your object, JavaScript actually replace this property with a specific function. That is why you need to change the name of the property and -- by convention -- we used the _
to do that. Otherwise, you will have an infinite recursive loop. Thus, when we do myQckd.weight = 15, it is actually the setter that is called, not a value affection for the property! More information about getters and setters here.
Your class can represents objects aiming to be interact with in the view of your application. Consequently, maybe some of its methods need to be called when specific events are dispatched. The inconvenient with JavaScript is that this
is dynamic. Therefore, we cannot "simply" give a method as a callback to a function.
This is called "losing this
". There is several approach for fixing this issue, but an elegant solution is relying on class fields, since they are object based, and not prototype based. Thus the method myClick
will be created on a per-object basis, with this
always referencing the object.
This is a recent implementation and could not work with all the browsers.
It is possible to associate a method to a class itself, not to its prototype
with the keyword static
. Usually, static methods are used to implement functions that belong to the class itself, but not to any particular instance (object) of it. Simply prefix the method of the keyword to obtain such a behavior.
This goes the same for static property.
Again, mind this
! It refers to the class itself, not a particular instance. Therefore, if we had a static method in our Quickdraw
class, and inside the method, alert(this === Quickdraw)//true
.
OOP imply visibility for fields class. In JavaScript, because it is a prototype based language, all the fields are public
by default. That means all properties and functions of an object are visible and modifiable anywhere if there is a reference to this object.
Protected fields are not implemented in JavaScript on the language level, but in practice they are very convenient, so they are emulated. In our Quickdraw example, weight
and brand
, as well as method1
are public.
The convention (since it is not implemented on the language level) is to prefix the protected element with an _
(underscore). So, if our protected should be protected, they will become _weight
and _brand
.
Do not be fooled! They are still publicly accessible. But since you will be probably looking for weight instead of _weight, you will not find them. You will also avoid all properties starting with _
in iterations instruction like for...in
.
The same goes for methods. Prefix the class methods with _
to indicate they are protected. However, tend to favor get and set when possible!
In the recent additions of the language, a private symbol as been introduced at the language-level. Any element prefixed by #
is meant to be private, and therefore is only accessible from inside the class whether it is a method or a property.
Relying on #
while defining a class element also has an impact on its syntactic declaration: two elements (e.g. properties) can share a same name if one is either public or protected, and the other private. You can also combine this with getter and setter to have a powerful encapsulation.
The instanceof
operator allows to check whether an object belongs to a certain class. It also takes inheritance into account. In case of polymorphic objects, for example, this operator is handful. It is applicable to class, but works also with constructor functions.
The syntax is simple.
It returns true
if myObj
belongs to the class ClassName
or a class inheriting from it.
Another way to bing method of an object with any event is by relying on async
and Promise. async
can be added in front of class/object methods to make them return promises, and await
promises inside them.
This is a brief introduction to meta-programming via Proxy
and Reflect
new features introduced in JavaScript by ES6. Meta programming is about code that have a direct effect on the code we use to write the application (or core).
The Proxy
object enables you to create a proxy for another object, which can intercept and redefine fundamental operations for that object (cf. MDN). In other word, it allows us to create some kind of a layer between an object and its consuming entities. We can then control the behavior of the object, like deciding what should be done when accessing a property in a object or the set
is used.
The syntax is:
Where the target
is the object for which you want to create the proxy, and the handler
is the object defining the custom behavior. The handler
traps some predefined functions, such as:
apply()
construct()
get()
has()
set()
With the above example, we saw that each time we access an object property, we call the get
handler
and perform the instruction within, instead of directly accessing target attributes.
This example is somewhat identical to the use of get
from a class
as we saw before.
Proxy can be used to make some quite elegant solution for protecting property access. You need the traps on get
, set
ownKeys
and deleteProperty
to check if the property start with _
. If this is the case, then throw an error.
Reflect
is a built-in object that provides static functions for interceptable JavaScript operations. Some of these functions are the same as corresponding method on object (yet subtle differences can apply). Put in other words, it simplifies creation of Proxy
since it is a minimal wrapper around the interal methods trappable.
Some examples
If you want to test your knowledge about Reflect API and Proxy API, you can go here. There is also lot of other cool stuff on the net! Go check out!
What is the big deal with events? It is that they illustrate how your code can run asynchronously! And this is a very important aspect for a client side application. Indeed, without this asynchronous aspect, waiting a button to be clicked will block the entire page, thus making it unbrowsable. More generally, code runs straight along, making instruction after instruction ; however, if your instruction is quite important and cpu-intensive and take some time to finish, the whole experience will appear to be frozen for the user, unresponding even to its clicks.
This behavior is called blocking : an intensive chunk of code runs without returning control to the browser, and therefore the browser appears to be frozen. The reason is that JavaScript is single threaded.
Even if JS is single threaded, this does not mean that it can't be asynchronous!
Asynchronicity allows us to initiate actions at a specific time, and finish them later without worrying to call them again.
Working with an asyncrhonous requires to change the way of how your think about your code and how it is supposed to run. Otherwise, catastrophes are on the way. Thus you need to be fully aware of the code execution order. The example below will console the first and the last log ; the second being block until someone click on a button:
A callback function is a function (generally anonymous) passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action. This is extremelly usefull to make versatile function by attaching it a specific behavior when it has finished to run.
We have already seen callbacks a lot in the Events section!
Callback are not always associated with events though. They can also be used with simple function (native or custom). For example with Timeout
Or custom functions
It is also possible to use callback functions inside other callback functions and make a whole chain of actions triggering automaticaly (e.g. on click of the button, perform an action then draw).
Be carefull of the nesting of callbacks, since it can quickly become out of control if the depth is too important. A way to alievate this problem is to create small standalone function and do not rely on anonymous function.
Promises have some similarities with callbacks. They are essentially a returned object to which you attach callback functions, rather than having to pass callbacks into a function. However, promises are specifically made for handling async operations, and have many advantages over old-style callbacks:
Chainning multiple async operations together is simple and easier to read (including the passing of the result to the following promise)
They assure consistency in the event queue
Error handling is better
They avoid inversion control
Concretely, the constructor syntax for a promise looks like:
When new Promise
is created, the executor runs automatically! For now, this is not so powerful ; below you will see why this is very important.
A promise involve always two "entities" in your code, and bind them together. The concept is the following, there is the concept of
An executor
, that does/returns something and generally take time. For instance, retrieving data over the network according to your REST API.
A consuming code, that wants the result of the executor
once it’s ready (not before! Because it is useless).
The executor
's arguments resolve
and reject
are callbacks automatically provided by JavaScript itself (no need to create them). They should always be present in the definition of the executor of your promise. When the executor obtains the result, be it soon or late, doesn’t matter, it should call one of these callbacks:
resolve(value)
: if the job finished successfully, with result value.
reject(error)
: if an error occurred, error is the error object.
(Please mind the three differents states of a promise, as well as the value of the result
property) Now, let's illustrate how this works with a simple executor
code using setTimeout
.
After one second of “processing” the executor
calls resolve("done")
to produce the result. This changes the state of the promise object from pending
to fulfilled
.
This goes the same for the executor rejecting the promise with an error:
A promise which is either resolve
d or reject
ed is called settled.
There can be only a single result or an error. The executor should call only one resolve or one reject. Subsequent call to resolve
or reject
are simply ignored : any state change is final. HOWEVER, instructions after the call to resolve
or reject
are still performed.
resolve
/reject
expect only one argument (or none) and will ignore additional arguments.
As stated before, a promise bind an executor
with a "consumer" which will receive the result or the error from the executor. A "consumer" is represented by consuming functions that are subscribed to the promise using one the three following methods/handlers:
.then
.catch
.finally
The .then
handler is the fundamental one for promises. It dispatches the executor's result
variable to the appropriate consuming function according to the executor's state
. The syntax is:
The first argument of .then
is the function to run when the promise is resolve
d, and takes the result
. The second argument is the function to run when the promise is reject
ed, and receives the error
from the executor. It can be used as follow:
In case you don't ever handle error in your promise, you can ommit the second parameter like and simplify the writting like : promise.then(alert);
because alert only take one argument.
The .catch
handler is designed to simplify the retrieve of the error if you are only interested by it. Instead of using .then
with a null
value for the resolve
like .then(null, errorHandlingFunction)
, we can use .catch(errorHandlingFunction)
which does exactly the same.
The call to .finally
will always run the consuming function associated with when the promise is settled (i.e. has been resolve
d or reject
ed). It is a good handler for performing cleanup (e.g. stopping our loading animations, etc.).
.finally
has no arguments: we do not know whether the promise is successful or not. This should not bother you because the kind of operations here are supposed to be "global", like stoping loading animation, etc.
.finally
does not process any promise: it passes through results
and errors
to the next handler.
Promises offer a really convenient way to deal with a sequence of asynchronous tasks to be performed one after another, which is call chaining. The idea beyond chaining is that the result is passed through the chain of .then
handlers.
The chaining process is possible because a call to promise.then
returns a thenable object (quite equivalent to a promise). Therefore, we can also call the next .then
on it.
The triggering of the chaining is asynchrone, but even if the two last executors looks like they run in a synchronous way, they don't. They indeed wait the previous executor to finish, but if you are writing code below a .then
handler the code will run probably before the handler.
You can also reintroduce asynchronicity in the chaining by creating and returning a new promise.
Let's see another, more concrete, example with the fetch
method (more powerfull and flexible than XMLHttpRequest
). We will retrieve my GitHub avatar, by finding my name in the commit history of the CDAW project on github, then display it for 3sec before removing it.
Do not forget that you can split your code into reusable function! For example, one for retrieving the GitHub username.
Remember, anyway, that : Promise handling is always asynchronous! Even if a .then
wait the previous .then
to finish, all the rest of your code is still running!
The async
keyword, put in front of a function declaration, turn the function into an async function
. An async function
is a function that knows how to expect the possibility of the await
keyword being used to invoke asynchronous code. An async function
in fact returns -- automatically or not -- a promise.
Is equivalent to
So, async
ensures that the function always returns a promise, wrapping anything non-promises in a promise.
The await
keyword works only inside async function
s. It makes JavaScript actually wait that the promise's state settles and return its result.
The syntax looks like:
The important thing to note is that the execution is "paused" until the promise is settled. That means that the code below the await
instruction will not be executed until the promise is finished.
await
doesn’t cost any CPU resources (unless the executor consumes CPU, ofc), because the JavaScript engine can do other jobs in the meantime: execute other scripts, handle events, etc.
Roughly speaking, it can be seen as a more elegant way to deal with .then
handlers.
To conclude, to call an async function
from an non-async, do not forget that you receive a promise!
Handling error for async
/await
function is relying on try...catch
syntax, instead of the .catch
handler.
In a complex web app, where interactions between users exist, you will probably need to synchronize several users together. This occurs mostly when a user performed an action, which change the state of the app, and this change needs to be refleted on other users. In a standard web architecture, the sever cannot take any kind of initiative per se, it waits for client to request information. Therefore, the synchronization of users must be thought of in the client side.
On a client-server architecture, the server is the final authority regarding logic and decisions.
Accordingly, you will need three steps to achieve such a synchronization, relying on asynchronous communication:
Send a POST
request to the server, indicating that the state of the app (e.g. the game) has changed
Update, in the server, the state of the app with the newly received state from the client (remember to perform security tests!)
All clients should ask regularly the server if there is any change in the app's state. If so, retrieve the change and reflect it accordingly
setTimeOut
is very usefull to achieve this behavior. However, keep in mind that setTimeOut
, as well as all time related function, are not reliable. Even if you set a specific amount of time t
, the wait will be t+Δt
. Thus, you will potentially face desynchronization issues with such a method. This kind of problem has been talked a lot on the internet. Here is an interesting post and reflexion about this topic in JavaScript, wrote by Gary Weiss.
An alternative is to use websocket. This way, the server can dynamically send information to the clients who had open a communication with it.
In this section, we will see the principal Web API client-side which are built-in the browsers. As a reminder, API (Application Programming Interfaces) are a way to simplify the construction of complex functionnalities by using abstract, high-level, functionnalities. The principal API we will use are the DOM API, used to manipulate the DOM for your page.
For your project, you will probably need the four following API:
DOM API
Fetching API
Drawing API
Audio API
Generally speaking, an API uses JavaScript object to handle the data from the API. This means that you can manipulate these objects in your code and access API's data easily by they properties and their methods. For example, retrieving a document on the page Object.getElementById(id)
or playing a sound SoundObject.play()
.
Most of API are events based, and handle events to change their inner state. Thus, they heavily rely on callback functions to behave correctly according to specific situation in your code (e.g. xmlHttpRequest
object). Check the Event section for more information about callbacks.
When manipulating an API, you should identify where its entry point(s) is(are). An entry point is generally an object representing the API's data, where most of the functionnalities of the API are available and where you can begin to work with.
Obviously, the entry point for the DOM API is the Document document
, or an instance of an HTML element that you want to affect in some way. For the 2D API Canvas
, its the <canvas>
element and then calling CanvasElement.getContext()
.
When you are learning a new API, check for its entry points first. They will help you to understand the "flow" of the API.
As you already know, the content you see in your browser is a set of HTML, CSS and JS. HTML is the content of your web page, and has a tree structure in order to be easier to be manipulate. This tree structure is called DOM.
In the above tree example, we can highlight important concepts:
Element node: It is an element as it exists in an HTML file (e.g. <p>
)
Root node: The top node in the tree, which is always HTML
for HTML file
Child node: The direct node of a chosed node
Descendant node: A node contained inside a chosed node, independantly of its depths
Parent node: The node of a descendant node
Sibling nodes: Nodes that are in the same level in the DOM tree
Text node: A node containing a text string
JavaScript has been made principally to manipulate the DOM of a page.
DOM elements retrieval is more than a common operation: it allows you to get the element to listen to, the element you want to update and so on. There is a lot of way for selecting DOM elements in JS. We will see the more common and convenient here.
Old selector functions are getElementByClass
and getElementById
. They select specific elements in the DOM according, respectively, to their class or their id. They are used with a direct reference with the document.
In more recent JavaScript version, querySelector
has been introduced. It is more powerfull and versatile than the getElementByXXX
since it combines all these functions in one. Concretly, querySelector
relies on CSS selector to perform the search in the DOM tree, somewhat unifying the selecting keywords in JS and CSS.
The above example can be written as follow using querySelector
Consequently, since you can use CSS selector in querySelector
, you can now make complex query (taken from MDN)
You can manually add (therefore creating) new elements to the DOM using JavaScript. To create programmatically a new HTML element, the keyword is createElement(ANY_HTML_ELMT)
, and it is used like that:
Now, we just need to attach this new paragraph to our document. Let's say that we want to attach it to the main div of our page, then we first need to select these div, and attach its the new paragraph.
Applying several time appendChild with the same object will not duplicate it in the reference node. If you need to duplicate an element, use the .cloneNode
method.
HTML element can have attributes (e.g. an id
, a src
). Since getting DOM elements returns us objects, we can directly access these attributes in their properties!
Sometimes, though, the attributes attached to an HTML element can be non-standard (i.e. either not expected for the element, or does not exist in the standard). For non-standard attributes, their corresponding properties are not created in the object!
However, you can access/modify/remove such non-standard attributes by using the following methods:
.hasAttribute(name)
.getAttribute(name)
.setAttribute(name, value)
.removeAttribute(name)
When a standard attribute changes, the change also reflects in the corresponding property. The other way is also true. So when you perform a elm.setAttribute("attr", value)
, the changed is reported back to the HTML element's attribute.
However, their is (ofc...) exceptions to this. For example, the input.value
synchronises itself only in the attribute -> propertye way, not the other. That means that you cannot perform a .setAttribute("value", "someVal")
on an input element.
Non-standard properties are usefull to pass some specific data to your HTML elements. However, since the HTML evolves continiously, it may happen that your non standard property becomes a standard property, possibly making huge mess in your code.
That is why to avoid conflict dataset has been introduced for the data-*
attributes. All HTML object has a dataset property listing all the data attributes an HTML element has.
All attributes starting with “data-” are reserved for programmers’ use. They are available in the dataset property.
For example, if your element has an data attribute named data-family
, you can access it:
Multiword attributes like data-my-family
become camel-cased: dataset.orderMyFamily
.
In addition, data attribute can even be used to store other object (use it with parsimony). Since they are HTML attributes however, we can't store an object reference directly, we have to transform it in a compatible string. That is good because an object can be encoded as a JSON object, so with can do the following:
It is also possible to manipulate the styles of your DOM elements! More information here.
In a web component paradigm (we will see that with Vue.js), an important aspect is the encapsulation of markup structure, styles and behaviors: they should nenot be leaking between components. However, due to how the DOM and the DOM API are made, this is not possible by simply using the standard DOM specification.
That is why the concept of Shadow DOM has been introduced. Shadow DOMs are hidden DOMs, not parsed by the DOM API, and only sensible to what happen inside them. We will cover this aspect in more detail in the Single Page Application section.
More information about shadow dom here.
Form and control elements are one of the principal source of interaction between your user and the server. That is why they have special properties and events attached to make their use more convenient in JavaScript.
Despite forms can be retrieved as any HTML elements using selector (e.g. querySelector
), they are also member of a special named (and ordered) property collection of the document
, called forms
. And, each form of this document.forms
collection has a named (and ordered) collection elements
, listing all the elements contained in the form. Therefore, you can then easily access any form and form's elements in a page simply by name.
No matter how a form element is deep in the form, it is listed in the elements
property of the form object.
If you organise your form with <fieldset>
element, you gain even more control on how your form is structured. Each fieldset
element also has an elements
property listing all its child form elements. And since a fieldset
is a form element, it is also retrievable by its name.
In case of Fieldset, that means your fieldset's elements exists both in the form.elements
and in the fieldset.elements
!
There is a backreferencing for each element of a form. That means you can get the form where the element belongs. Simply use the form
property of the form element.
Form control is related to accessing, setting and checking form's elements value. To access their value, we can use the value
property. Note that its content depends obviously of the type of input checked.
You can also set the element value.
Generally speaking, the information of interest in an element form is stored in the value
property, not in the innerHTML
.
So far, we have seen how to access forms' elements. However, this is not really useful if we did not know when to check for their value! Luckily, JavaScript trigger a lot of events when elements are used.
Every time an element's value is modified by the user, this event is triggered.
The input event does not only trigger with keyboard events, but also with other device, such as mouse (e.g. pasting a text from the clipboard) or with text recognition device. However, non altering action, such as pressing ^
alone (or directional arrow) does not trigger this event.
This event is good if you need a fine management on the element.
However, we often don't need such a fine management over an element. We prefer to check the element once it has lost focused. The event is change
and can be used as follow.
If you want to check if a name is free or already taken in your form for example, you will want to wait until the user leaves the field ; otherwise if you use the input event, for each input, you will interrogate your server which is a total waste of resources!
The submit
event triggers when a form is submitted. This means that either an input of type="submit"
as been clicked or enter has been pressed in a field. This is usually used to validate the form before sending it to the server or to abort the submission and process it in JavaScript.
Never ever trust the user side of your application. It is possible to get rid of your data control. User side verification should be design with reducing the server side load in mind, not replacing it!
However, the submit
event is not attached to the submit input, but to the form!
The method submit()
of a form allows to initiate form sending, from a JavaScript standpoint. This is useful to manually send our own forms to our server.
When using the submit()
method, no submit event is triggered! Keep that in mind.
The need of dynamically refreshing some part of a web page, and not the whole page, is an important feature in the web now. Indeed, from a server standpoint, sending just some chunck of data is often a more lightweight operation, and from a UX design point of view, the flow stay consistent: only the elements that are supposed to change are actually updated on the page, creating dynamic web page.
Nowadays, you use JSON instead of XML.
In a conventional web application, for retrieving specific elements, a new whole HTML page has to be received. This is not the case whith an asynchrone communication. The most known approach is AJAX. It works as follow
However, this is somewhat similar to all the asynchrone approaches: the DOM is updated asynchronously. In any case, the server should obviously be designed to handle such asynchrone requests.
Asyncrhonous JavaScript And XML (AJAX) is a programming practice designed to build dynamic web pages relying on asynchronous communication. It uses the xmlHttpRequest
to communicate with the server.
However, we will not study this technology, since the fetch API has been recently introduced and is planned to replace it. Nonetheless, almost all the current web site use AJAX for now, so it is important to understand what it is and how to use it.
The Fetch API provides an interface for fetching resources (including across the network). It will seem familiar to anyone who has used XMLHttpRequest, but the new API provides a more powerful and flexible feature set. (source: MDN)
Put in other terms, the Fetch API is a wrapper, a generic definition on how systems request resources and how they respond, and therefore can be extended to much more than network communication.
The basic syntax for fetching resources from a server is simply:
Note that fetch
returns a promise, and without option, your perform a GET request, downloading the content from the url
. Getting a response is a two stages process :
The promise of fetch resolves with a Response
object as soon as the server responds with headers.
The response as to be parsed to gets its content/body.
In the first stage, we usually check the status
code (a property) of the headers received in the response
. The shortcut response.ok
is useful to check if the status code is between 200-299. If not, you know that there was an error during the fetch.
Abnormal error (e.g. 300-499) do not cause an error! Therefore the following code is not equivalent:
Because bad HTTP code will not be caught.
The seconde stage is to get the response body. In the above example, we saw that we do a response.json()
to get that body. This is possible because Reponse
give provides multiple promise-based methods to access the body. Some other examples:
response.text()
: read the response and return a string,
response.blob()
: return the response as a blob object,
response.arrayBuffer()
: return the response as an array buffer.
A body can only be consumed once! You cannot call consecutively response.json()
then response.text()
.
You can also make asynchrone POST
with fetch. To do so, you will need to specify the method, the header and the content (body
) of your request. The body can only be one of the following type:
a string
(e.g. JSON-encoded),
FormData
object, to submit the data as form/multipart
,
Blob
/BufferSource
to send binary data,
URLSearchParams
, to submit the data in x-www-form-urlencoded
encoding, rarely used.
(The JSON format is used most of the time)
Like that, you can for example send an image once uploaded in a form without reloading the entire page.
As you have noticed from now, JavaScript is a very flexible language, allowing you to perform things that is usually not possible in more conventional language like Java or C (e.g. non-typed variable and polymorphism). However, the drawback of this flexibility lays often in code instability and weird behaviors (this
has an important part to do here).
Exploiting the non-typed approach of JavaScript, a way to allievate weird behaviors is to heavily rely on return;
to stop the execution of a function that either does nothing to do, or has face a faillure/unauthorised case. To foster maintenable code, and well documented, the checkings is often made at the begin of the function -- regrouping all the verification in a single place which can be easily documented.
That is why you want to rely on get
and set
for your class! You can handle these scenarios and sometimes correct them with default behaviours (e.g. set a negative variable to 0).
However, no matter how hard you try, your code will fail one time or another (because of you, the user, the hardware, etc.). Despite return
is a good approach in several cases, sometimes you need more powerful error managment. Indeed, when your script fails, it usually "dies" -- stops immediatly. However, maybe you want to recover from this error and do some specific actions instead of stopping your script (e.g. reinitializing your interface).
My advice is to break hard and break soon! Even if your code growth in size and take you more time by doing so, this is always a good practice, and it will help you when your code will become important.
JavaScript, as several other language, introduced the try...catch
syntax:
When JavaScript arrives on a try...catch
statement, first the code in try
is executed. If no error are thrown inside this block, then the catch
block is ignored. Otherwise, the rest of the try
block is ignored once an error is thrown. The error is then handled by the catch
block and stored in a variable (here error
). Consequently, an error does not stop your script of running if it happens in a try
block.
The catched error by catch
is also an object. The built-in erros has the following useful properties:
name
message
stack
Variable inside try...catch
are local! You cannot declare a new variable inside a try
block and use it elsewhere. This gives some confusion at the beginning, since it may change how your code is structured, especially with nested try...catch
.
There is an additionnal clause to the try...catch
which is finally
. If attached to a try
or a try...catch
statement, the content of the finally
block is always executed. This is usefull to end/finalise something that has been started. For example, closing a communication socket. The syntax:
The finally
clause works for any exit! That means if you return
from a try
or block
explicitly, you will land in the finally
block!
You can produce your own error during the execution of your code. For example, parsing a JSON string and noticing that there is no information about an expected state of a game (i.e. gameState
is set to undefined). To do so, we simply use the throw
keyword:
Error
is the super type of all the errors in JavaScript. But there is more specific built-in errors, like SyntaxError
, that can be used as Error
depending on the situation.
When you throw
an error, you create an exit point in code: all the instructions below are no longer executed (like a return
).
In a complete example:
You can rethrow a handled error in a catch
. Rethrowing is used when the error received cannot be correctly handled where it happens and needs to go back up the execution stack.
You can define your own errors by extending the built-in errors in the language, like any class. And then use it as any other errors:
A good practice in your custom error could be to keep a reference on the object which has failed.
Since MyError is a class, you can inherit from it too!
An important things to note is that try...catch
works synchronously. In other words, if an error happens in a "scheduled" code, like in a event callback or in TimeOut
, try...catch
will not catch it. That is because the function is executed later in the code, when the engine has alread left the statement.
Thus, to catch an error in an asynchronous, the try...catch
need to be in this very function!
In addition, in the case of a async/await
situation, it is possible for a promise rejects an error, just as if there were a throw
statement at the line of the .reject(new Error)
.
However, in real situation, the promise will take some times before it rejects. In that case, we can use try...catch
to catch the error throw by await
ing.
You can wrap multiple await line in one try
as showed above!
If you don't use try...catch
, you can still catch the error by using the .catch
statement when f() becomes a rejected promise: f().catch(alert)
.
There is three things to figure out for rendering a component :
Where this component will be placed on the page and its objectif
What are the DOM command that can be used within the template
How to setup the reactivity of your component, so that it becomes dynamic
In this section we do not focus on binding data yet. Check the for this kind of information.
Binding consists of linking data in the <script>
section of your component with the <template>
section of the same component. In Vue.js the simpliest way to bind data is to use the {{ Mustache }}
tag (two {
, your data, then two }
). While the component is rendered, the tag will be replaced with the content of the data used within the mustache.
Mustache can handle complex JavaScript operation too
Everything in the mustache tag will be interpreted! You cannot put html elements in there.
Nonetheless, mustache tag has some limitation, especially within the HTML attributes since they cannot be used. For example, you can't write <div id="{{myDynamicId}}">
: this will throw an error. To circumvent this issue, Vue.js introduces directives which are special-interpreted HTML attributes.
The most important one is the v-bind
directive. This directive allows to bind a standard HTML attribute element to a custom data. For example, you can bind your data with the id
of an element using the v-bind
directive.
There also exists dynamic binding, which allows you to use varibale to determine which HTML property you want to bind your data with. Check the documentation if your are interested.
We will see in this section some handful syntaxex to define how your component should render according to its state (its data).
In your component template, you will often face the necessity to check if a variable exists in order to display specific information. For instance, you want to display user's information only if he/she is logged in your application, otherwise you don't have access to this information.
To do that, Vue.js introduce the v-if
directive. It is used to conditionally render a block: the block will be rendered only if the expression value is computed to true
. You also have the v-else-if
and v-else
directive, obviously.
A directive has to be attached to only one element. If you want to toggle more than one HTML element with you directive, you must use it on a <template>
element, which serves as an invisible wrapper.
There is also the v-show
directive used to conditionnaly render a block. The cost of the rendering operations is not the same. v-if
always destroy its content if the condition is false, while v-show
relies on CSS property to hide the content thus don't have to recreate the all content if the variable becomes true again. If you have costly drawing often toggling, maybe v-show
can be better.
Another common needs is to dynamicaly render list of elements in a page. Vue.js introduces the v-for
directive for addressing this need. It uses a special syntax item in items
(if items
is the array, item
is an alias for the array element: it can be anything else). You can also retrieve the key/index of the element iterated with the special syntax (item, index) in items
.
You can use v-for
to render a list based on an array.
Here, a first <li>
element will be rendered with the Foo message, and the second with the Bar message.
It is also possible to iterate over an object's properties with the same directive.
Here, the first <li>
will display Name is: Soen, and so on.
Same as other directive, if you want to render several elements in the v-for
directive, you must use it on a <template>
.
Another important things for the v-for
directive is about maintaining the state of your list up to date. Whenever possible, it is higly recommended by Vue.js assign a unique key
to the alias element created (here item
and prop
) when possible (the value must be numeric or string). To do so:
When a Vue.js instance is created, it adds all the properties found in its data
object to the reactivity system of the framework. When values of those properties change, the view will automatically “react”, updating itself to match the new values. This is a powerful feature that you should intensively use (it is somewhat a reimplementation of the observer pattern).
Each component has a data
object. For every component but the main component Vue.vue
, data must be a function, otherwise, the data will be shared accross the difference instances of the same component.
In the <script>
element of your component, you should have:
Then, during using app, if you change the value of name
, this change will reflect on the template of the component.
Lifecycle hooks are special functions which allow user to perform specifics operations at certain moment of the creation and the rendering of a component. For example, just before your component goes mounted
to the page, you can modify the component's data.
Below a diagram of the lifecycle of a component and the associated hooks.
Though you can use camelCase naming convention in the declaration of your component, or even the component's variables, you need to reference to them using a kebab-case (hyphenation) convention in the template of your component!
We will see how we declare a component, how the data flow is configurated, as well as how events are handled.
Registration is making your component known by your application so that it can be used during rendering. We will not see global registration here. Check for more information.
Instead, local registration, in addition of providing some advantage about the weight of your app, allows a better organisation and uses the pre-defined model given by the CLI scafolder you used to create your Vue.js project. Lets review the step needed for registration.
As you can see, your Home.vue
component has the following code in <script>
In the main.js
of your file, you should have something like (if you used the configuration with router)
Stop! What is happening here? Well, in this specifc case, we register our HelloWorld
component into our Home
component: it is then available to use. The new Vue
here create our Vue application. The specificity here is that we specify the router
component (automaticaly created with the CLI with routing configuration), which in turn register (well, in a specific way for routing purpose) Home
.
So, every of the components you plan to use in a component must be declared in the components
object of the host component.
We can use a component only if it has been export
ed. To do so, you need to use the export
keyword in the file where the component is described.
Exported modules are in strict mode whether you declare them as such or not. The export statement cannot be used in embedded scripts.
And to add a component to the array of components
, this component needs to be import
ed! When you use the import Obj from 'my/path/to/Obj.vue
, you create an instance of this object.
In a real application, you will often have components which will depend on specific data -- data most of the time provided by their parent. For example, lets imagine that your main App
has a user
object. We want to pass this object to the userSetting
component, in charge of displaying its avatar, and all its information. Passing off the data could be as follow:
The parent gives the object userObj
to its child. To receive such a variable, the child component must define its interface (called props
in Vue.js), namely all the receivable variables and their alias. In the above example, the alias for the attribute is user
. Defining the interface of your child component is by filling up its props array:
This is called a One-Way data flow. The flow goes from the parent to the child. If the value is changed in the child, the changes are not reflected to the parent. However, if the value of the object given to the child component change in the parent, the change is reflected in the variable of the child. This default behavior prevent uncontrolled mutation from the state of the parent.
You can perform prop validation by specifying type requirement in the props
attribute of the component, as we perform above:
means that user is an object, and should not be otherwise. You have different keywords available, such as String
, Boolean
, etc...
Tend to prefer this notation for the props
! It is always a good idea to introduce some verifications in your code!
If you face issue with reactivity with your propos, check the following list, maybe you fall into one of these case:
primitive props (String, Number, … ) can be reactive in a component only if made dynamic via v-bind:
they are reactive if used in computed properties or in methods
they are not reactive if used in data as initial value - unless being referenced by a watch function. Also note that when used as non-reactive initial data the assigned value will be the props value at the moment of component creation*.
Object (or Array) props can be reactive whether or not passed via v-bind if the object itself is being passed and stored in data - and not just primitive values of the object.
If the Object reference is stored in data any use of this data will be reactive towards the props value
If the Object is cloned or only primitives are stored in data the same rules apply as for primitive props above
HTML has never be user friendly regarding its reusable aspect. In complex page structures, where lot of UI custom are placed, the document can quickly become a mess (mostly due to a lot of copy paste, modifying few ids and classes). However, as developpers, we know that reusable and small code is a good idea, if not only for code maintenance.
Web components is a suite of technologies that has been created to address this problem of reusability in web page. The idea behind web components is to break a web page into small and reusable blocks, with their own custom logic and states, where they can communicate between each other, mostly via events and recativity (i.e. when an object's attribute change). This allows a better encapsulation and help the developpers to seperate the view from the application logic (the model).
If we take a look at a web page, we can see that some elements are redudant and can be derivated into web components (and maybe it is already the case here!).
In a web component approach, such a page could be transcripted into the following HTML code:
To achieve this building philosophy, web components rely on shadow dom, preventing them to leak outside their respective DOM. Thanks to dedicated JavaScript framework, Shadow DOM manipulation is often transparent to the programmer.
Vue.js components are written as a combination of JavaScript objects that manage the app's data and an HTML-based template syntax that maps to the underlying DOM structure.
As we have seen above, a web component is an encapsulation of one or several specific HTML elements (native and/or custom component) supposed to work together in a specific way in order to make an action or an application logic easily reusable. A web component is a custom HTLM element, generally "reactive", which is usable as any other HTML element in your document. But two instances of a same web component can have a different state.
This last fact implies that a web component definition introduces some variabilities in its HTML code. Such an adaptive code is called a template. It describes how it should be rendered (from an HTML point of view) by identifying which part of the HTML is static, and the other part is dynamic. This allows the web component to update itsefl depending on the current state of its data, consequently reflecting the change to the user accordingly.
An example of a template, in my-logInfo-component.vue
:
Here, we specify a component designed to display user's information, and if connected, a button to disconnect it, mostly by using HTML code plus some specific attributes (we will see them later). Let's say we have two user now, we could have two instances (or more!) of this web component in our page:
This is just an illustrative example. The syntax in Vue.js will be a little different.
As you can see, we somewhat "paste" the content of the web component's template -- instanciated -- inside the custom HTML. Thus, during the rendering, the HTML is adapted to the state of the component (like PHP in some way, except this is dynamic).
If you are wondering, the logic of your web component is directly associated to your view/template. The way the association is made depends of the framework used. In any case, you can privately use the data and method you declare in your component, and handle any events.
Registering attributes is the concept of passing existing data to a web component, and storing the passed data into the component (to use it). In the example above, we bind the object user1
and user2
to respectively the first and the second <my-logInfo-component>
components. Registering data to components is useful to instanciate them correctly. Like that, you can pass a data from a parent to a child component, again to a child component that needs specifically this data to correctly instatiate.
Moreover, registering data allows a powerful mechanism to take place: reactivity (even accross components). The reactivity consists of two things. Firstly, it kept the registered data synchronized. Secondly, and this is powerful, it listens to any change made in the parent data, and re-render the components which depends on it. This makes the page dynamic and keeps all the element synchronized.
There is also the two-ways binding/registering, which simply consists of listen to any modification for the parent data, or the child data. Thus, once the data change, from either component level, the modification is taken into account, and the component re-rendered. Thanks to that, it is also possible to share data accross component not only in depth, but also in wide.
Check the illustration below. We have a main page where three components, where two are different instances of the same component, are attached to the main page. On the first blue component, we also attach a third component. Big arrows represent registering, thin arrows represents cross registering.
Vue.js adopts a much more simple approach than some heavy framework like Angular: everything a component needs is write into a single file. Let's see the structure of a Vue.js component.
Has you can see, there is three parts in a Vue.js component. The <template>
section, that we discuss about, the <script>
section handling you component logic, as well as all data reactivity and some specifics mecanisms and the <style>
section, used to design your component.
In this section, we will learn the concept of Single Page Application (SPA), how to make a SPA and how to dedicated client side framework.
A SPA is a web application accessed with a unique web page instead of loading entire new pages. It interacts with the user by dynamically rewritting some parts of the current web page by retrieving data from the server. All necessary HTML, CSS and JavaScript are retrieved with a single page load (or dynamically loaded and added to the pages thereafter). There should not be a reload of the page at any point of the use of the SPA, nor delegation of control process to another page.
SPA rely on dynamic communication with the server, either by using or websocket to modify elements on the page. Below a comparative illustration.
SPA tends to favorise a better maintenance, and a better referencement. They also emphasize a stronger organisation of your application and offer increased performances, mostly when the server's tasks are heavy since a lot of logic is client sided.
One major drawback of SPA is they reliance to JavaScript. If your user has deactivated (or cannot correctly interpret) JavaScript on its browser, your site is unusable (or worst, can have some security flows). Keep that in mind, especially when you strongly couple SPA with web components, since the web components standard is not well implemented in all the browsers.
In this course, we will use Vue.js as our JavaScript framework for building SPA. There is a lot of other JavaScript frameworks out there, such as:
Vue.js is a modern JavaScript framework that provides useful facilities for progressive enhancement and strong reactivity. It is one of the most easy to use framework and lightweight. It also adopts a "middle ground" approach, instead of more important framework like Angular, where e.g. route and state management are delegated to other (already) existing libraries doing the work quite well. And unlike many other frameworks, you can use Vue.js to enhance existing HTML.
Usually, a JavaScript framework allows you to handle the DOM of your page more easily by giving you new ways to manage it, create reactivity (i.e. using a built-in observator pattern for your object's properties) between various HTMLxJavaScript elements and providing you with usefull new API.
Using the important method is good for small project and tests. But it will quickly become not practicable! Use the CLI!
Instead of rewritting the procedure, it's demo time for creating a new project!
🕑Time passes🕑
Your src/
folder contains all the Vue app and components you create. src/App.vue
is your main vue (top-level) component, which globally load the framework. The folder src/components/
should contains all your web components. src/assets/
is used to store static assets like CSS and images. src/routers
contains your route configuration for a SPA, and src/views/
contains all the vue (the "pages" or sections) of your SPA.
Install the plugin Vetur
for VS Code for code highlights!
Dynamic routing is the heart of any SPA application. Instead of changing and loading a new web page when the website URL changed, the idea is to update the page by adding and removing new component on the page. To do so, The HTTP request is caught by a dedicated router
component, and if the desired URL match the routing rules of the router, then components are invoked and destroyed in order to update the page.
The official Vue.js router documentation is accessible .
In order:
Import and global configuration for your router component. You need vue-router
to be able to route HTTP request. Don't fort Vue.use
to say Vue to use the router!
Define some routes. Each route should map to a component. So a route has a path where it trigger (e.g. when writting server/url/about
) and the component to call.
Create the router instance and pass the routes
options
Export the router (it is used in Vue.js, go check there!)
The important thing to note here is the path
property. This property indicates which queries has to be caught -- any uncaught path generate a new server call. The /
indicated the root of your website. Therefore, when accessing the index of your website, you will instead load/render the Home
component ; when accessing the /about
URL, it will be the About
imported component.
In addition, when an URL maps a route definition, the component(s) specified in the components
property will somewhat "replace" the <router-view/>
component. Think of it as an anchor.
That's it! Do you see the advantage of web components now? Each page relies on either one or several components!
## Dynamic routing Most of the time, your website will access very similar resources, discriminated only by a specific information. For example, you can access your users Sol
and Ky
with the /user
URL suffixed with their pseudi, like in /user/sol
and /user/ky
.
In a web component approach though, this will be the same component that will handle this data processing and rendering (and request). So we need a way to map several routes to a same component: this is dynamic routing. To do so, we can use dynamic segment in the path expression:
You can have multiple dynamic segments in your url. Say that we want to retrieve a specific game information (the 315th) of the user Baiken
, the dynamic path could be:
The content of the $route.params
here will be {username: "Baiken", id_game: 315}
.
As we have seen, we can use $route
both in the template of your component, and also in the JavaScript of your component. However, this may create intricate coupling between your route and your component, and this is not very good for maintenance and scaling. A good way to do so is to pass the $route.params
as props
for your component.
Here is how you can do that:
Thus, your componant can now be used everywhere in your app: its interface is clearly specified -- it waits an id
. The true
boolean in the route declaration is used to transform your params
into props
. You need to set it to true
to use this feature.
In complex application, the page is often composed of several components nested at various levels. For example, we could imagine that accessing the games information for a specific user is still made in the user component, as well as accessing its profile.
The routing logic here should not be entierely made at the top level, but instead to the component-level. The top-level application should indeed redirect to the User
component, but any further route mapping should be handled by this component, such as accessing the profile
of the user.
Thus we could used the <router-view></router-view>
component to host the nested component and react according to the end of the URL.
To indicate that a component is supposed to be nested according to a specific route, you need to used the children
property of a route. Moreover, if you want to keep access to the parent page (here: /user/Sol), you will need to specify an empty subroute.
You can also programmatically (manually) navigate accros your site. Again, the doc is here for you if you need that.
In this section, we will see how you can conjuge an OOP approach with Vue.js
The first thing you need to do is to export your class declaration. If you have a one class per file project organisation, a Weapon.js
class could look as
Once your class is exported, you can still use it within other classes (or even inherit from them). For example, a NPC.js
class using a weapon:
Then, in a component, you can use this class. Example:
With all that, you have what you need to adopt an OOP approach in your Front End app! Have fun!
This is just an overview of event handling in Vue.js. For more information, check the and the .
Vue.js change the event listener (handler) paradigm, and how and where they should be introduced in the code. The frameworks encourages listener to be DOM-free manipulation and focus only on logic. To achieves this, the listener are now attached directly to HTML document using the v-on
directive. An advantage is it's easier to locate handler function by skimming your HTML template instead of the JS code, another is that Vue.js handle all the creation and desctruction of the listeners.
Easily enough, an event is handle using the v-on
directive, followed by :eventName
the name of the event to listen to. This is directly followed by ="method"
the handler to call for the specified event.
However, the method needs to be defined in the component using the methods
attribute:
We can even use event modifiers to change the default behaviour of an event, e.g. event.preventDefault()
for a form. Vue.js introduces a list of keywords related to the event modifier, and to use them, suffix your directive by the keyword, like v-on:submit.prevent="onSubmit"
.
You can fired custom event using Vue.js. To do so, you can use the this.$emit("myEvent")
function call. This will fire the event "myEvent", which can be handle with the v-on
handler.
Despite it is the case for almost everything in Vue.js, the name of an event is not transformed automatically into kebab-case name (my-event
) in the template. You must refert it by its actual name. Consequently, it is recommended to always use kebab-case for events.
Two-way binding, if badly executed, can cause undesired behaviors, time consuming debuging session and even disastrous performance. Use it carefully!
To reduce the impact of the mentioned issues, Vue.js strongly recommend the use of events to update a parent data. For example:
A good practice is to prefix your update event with update:
. Like that, you will know that this kind of event is only used as for the double way binding mecanism.
It's used as follow:
For convenience, Vue.js introduced the .sync
keyword which do exatcly the same thing, but is a shorthand for above (supposing that your event is called update:theNameOfTheData
).
https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier
In JavaScript, there always exists a special property for each object called [[Prototype]]
, that is either null
or a reference to another object, called a prototype. Prototypes are used to perform prototypal inheritance, which means than the prototype of an object extends this very object. In other, this allows an object to directly reuse properties and methods from its prototype automatically: this is transparent for the developper!
This property [[Prototype]]
is internal and hidden: you will not iterate over it with for...in
loop for example -- instead you will iterate overs the properties/methods of the prototype. Their is several ways to access the prototype, such as the historical __proto__
, but we will not use it anymore. Instead, we will use the class oriented methods, more clean: Object.getPrototypeOf()
and Object.setPrototypeOf()
.
Let's illustrate prototype setting and automatic access of the prototypal chain:
Visually, this give something like:
As you can see, there is one additional prototype, which is the Object.prototype
. Indeed, when you create an object, JavaScript automatically attach to this object the Object.prototype
in the exemple: this gives us access to a lot of useful function defined for all the object, such as toString()
.
this
Since this
is dynamic in JavaScript, an iterrogation may arise: what is the value of this
once in the prototype chain?
The answer is always the initial object calling the property/methods, not the objects from the prototype chain: this
is not affected by the prototype chain at all.
Use the following rule to help you find the value of this
: no matter where the method is found, this
is always the object before the .
(dot)!
What happens here is that the variable versus
has been attached to the calling object kendo
, despite the fact the function that creates it is in its prototype (martialArt
). martialArt
does not have a versus
variable after that.
The prorotype is only used for reading properties and methods, not for writting them. Write operation (as for delete though) works directly with the object itself. Thus, you can rewritte an actual function existing in the prototype by the object's own definition of the method.
This works as an override because now, JavaScript directly find the method you are calling: thus is does not have the need to go back up the prototype chain.
By now, it should be clear how prototype can be used for object inheritance. As stated above, this is called prototypal inheritance. Here we will see how we can extend an object using constructor function and new
operator by using a property named prototype
.
Here we speak of a special property called prototype
, not the [[Prototype]]
seen above. It is only used once: while construction an object via a function thanks to new
.
Class inheritance is a basic feature in OOP allowing to extends the functionalities of a class with ones from another class. In JavaScript, it is basically a syntactic sugar for prototype manipulation.
This is how graphically the above example can be represented:
Now, if we want to create a new class, lets say Planet
, that is based on Satelite
(i.e. that share similar concepts), we can use the extends
keyword to indicate so. The global syntax is class Child extends Parents
. In our example, this will result in:
Internally, extends
keyword works using prototype mechanism. It sets Planet.prototype.[[Prototype]]
to Satelite.prototype
. So, if a method is not found in Planet.prototype
, JavaScript takes it from Satelite.prototype
, as we have seen before.
While extends
is often used with class, you can put any expression after. That means a class can extends a function for example.
As said previously, when JavaScript is told to execute a specific method from an object, it firts checks if this object has a property of that name. If not, it goes back up the object prototype and checks in it, and repeats the operation while the property has not beend found or there is no more prototype to explore. That means if we define a new explode
method in Planet
, all planet object will have a function property named explode
: JavaScript will not bother to go back up the prototype chain. This is called overriding a method.
However, when overriding a method from the parent class, we often want to build on top of it, not entierely replace it. In other want, we want to call the parent method before (or after), then perform the class's specific operations. To do so, we use the super
keyword (e.g. super.explode()
).
super
can also be used to override constructor. Currently, Planet does not have is own constructor, it relies entierely on the Satelite constructor. But if your class needs to have a specific construction, you can't rely entierely on the parent class (which is probable more generic). Calling a constructor parent with super(...)
can only be done in a constructor.
A child constructor MUST always have a call to its parent constructor (super(...)
)! In addition, this call MUST be made before any property affection (e.g. this.x = smth
). This is logical, because you build your child on top of your parent!
When a class extends another class, and has no constructor, JavaScript create a default constructor, directly calling the parent constructor. The default constructor is the following:
As a consequence of overriding the default constructor, it becomes unavailable. Once you define your own constructor(s) (for an extended class), you can't call new MyClass()
anymore.
Private fields in the other hand are not inherited. There is no direct access to a private methods/attributes from a child to its parent. Consequently, you will need to rely on both getter and setter to retrieve the information, otherwise JavaScript will throw an error.
Such a limitation can be sometimes to severe. It is up to you to determine whether or not it is good for you code to use private fields over protected one, and all the implications that it creates.
Static methods and static properties are also inherited!
As we have seen, there is only one [[Prototype]]
for an object at a time. This implies that multi-inheritance is not possible using extends. Despite this prevents complex and dangerous inheritances (like diamond ones), this can feel a little limitating. To alleviate this, JavaScript implements mixin.
A way to implement a mixin in JavaScript is to define an object continue the desired useful methods, and then merge them to the desired objects, by using the Object.assign(source, mixin)
function.
A mixin is a class that contains methods for use by other classes without having to be the parent class of those other classes.
Mixin is a simple method copying process made by JavaScript: it does not consume the extends
slot.
Sometimes, you will just want to use a specific method from another class, and inheriting from it could be misconceptual (i.e. inheriting a Planet while the child is Pencil). It is possible to borrow methods from an object, by using the bind
, apply
and call
keywords.
Just a word on bind
thought. This very useful keyword allows us to set the value of this
to the provided value.
Hook example :
A constructor should never failled! If it fails, an error should be thrown, or the construction of the object should be delegate to a builder (cf. ): there should not be other possibility IMO.
Use your browser to debug your code!! A from the Chrome DevTools.
Two way data binding allows a child component to update its direct parent. It is mostly used for forms. Check for more information.
Note that objects and arrays in JavaScript are passed by reference, so if the prop is an array or object, mutating the object or array itself inside the child component will affect the parent state! Be mindful of your mutation (that is why you should heavily rely on getter and setter -- check )
At least, if you want two ways data binding, you can use the v-model
directive. Check and here for an . However, it is recommended to use .sync
instead alonside event. Check the for more information.
One thing to be noted though: your props
are not registered into the reactivity system of the framework. Here is apparently some rules (from ):
Shadow DOM is a functionality that allows the web browser to render DOM elements without putting them into the main document DOM tree. This creates a barrier between what the developer and the browser can reach; the developer cannot access the Shadow DOM in the same way they would with nested elements, while the browser can render and modify that code the same way it would with nested elements. However, elements declared in the shadow dom can still fire events that can be handled by other elements in the document ().
An important point is that SPA tends to rely heavily on the notion of web components (cf. dedicated ). Briefly, they are custom and reusable HTML elements embeding their own logic.
The installation procedure of Vue.js is explained in the . Here, we are interested in the CLI installation (there is a more detailed explanation in the section.), which gives us a way to quicly create a new projet and scaffold important project.
.
Currated list of various components and stuff for Vue.js:
If you have created your Vue.js application using the CLI as writen , then you should have a router component called index.js
in the folder @/src/router/
(@ is the root of your Vue.js project). Let's have a look
The navigation performed by the router component is lazy. That means if you navigates from a user (e.g. Potemkin) to another one (e.g. Sol), the same component instance will be used! This for performance consideration, however this can be a little constraining because your component may not react very well on change. To fix that, you should use the watch
method. More info .
You can even set a function which returns props. Check !
Maybe you will also have complex view in your application, and consequently several <router-view>
in one same component. You can name them and specify which view has to be updated when a path is matched. More information in
There is no hoisting (cf. ) concept involved here!
This property prototype
of a constructor function (cf. ) means the following: "When a new
object is created (by using the new
operator), assigns its [[Prototype]]
to the given object". By this mean, all the objects from a constructor function created with new
will all have the object as prototype.
Protected fields are not supported to the language-level. As so, the convention is to used the _
to prefix them (cf. ). A direct consequence of this is that protected fields are directly accessible from a child to its parents -- since they are just properties obfuscated.
From :
Go check online for resources! Like or .
Example (from ):
Made of several pages
Only one page
Web browsers generally just display pages and gives to the server the users' input
Web browsers fully exploited and dynamically retrieve content from the server
All the logic of the App is served-sided
Either the logic is client sided (beurk!) or separate as much as possible to not compromise security
Server reacts to user interaction and serves new page
The server is responsible to expose and make accessible app's resources (e.g. REST API)