Jonas: So now that you know how ES6 modules work,
I just wanna quickly show you the module pattern
that we used to use before
in order to implement modules in JavaScript.
And I believe that it's important
that you understand this module pattern
because you will still see it around,
and it's also a very good application of many of the stuff
that we have been learning throughout the course.
Now, of course,
just like in regular modules that we just learned about,
the main goal of the module pattern
is to encapsulate functionality,
to have private data, and to expose a public API.
And the best way of achieving all that
is by simply using a function,
because functions give us private data by default
and allow us to return values,
which can become our public API.
So let's see how the module pattern is implemented.
So we start by writing a function, okay?
And usually we write an IIFE, actually.
So an immediately invoked function expression.
And the reason for that is because
this way we don't have to call it separately
and we can also ensure that it's only called once, right?
And so for that, we wrap it like this and create an IIFE.
So it's very important that this function
is only created once because the goal of this function
is not to reuse code by running it multiple times,
the only purpose of this function
is to create a new scope and return data just once.
And then in here in the function,
let's simply add the same variables
that we had before in the other module.
So let's do here again, a shopping cart.
And so in that other shopping cart,
we had a bunch of private variables, basically,
so data that was not exported.
And so we simply defined them as variables there,
and here we do the same thing in the function.
So total price 237, and total quantity 23.
You can also use or add to cart,
and let's simply grab that from here.
So without the export, of course,
and in this one, let's actually add
another very simple function,
which will simply lock something to the console,
so this one is to order stock.
So ordered from supplier,
and let's call this one order stock.
So right now, of course,
all of this data here is private
because it is inside of the scope of the function.
And so now all we have to do is to return some of this stuff
in order to basically return a public API.
And so to do that, we simply return an object,
which contains the stuff that we want to make public here.
So let's say again,
we want to add the add to cart function to the public API,
also the cart array and total price and quantity.
So basically in this case,
everything except the order stock, okay?
And of course we could have also defined all of these here,
right in the object as properties and methods,
but I find it a little bit cleaner
to define everything outside
and then to simply create an object,
which contains everything
that we want to expose to the outside.
However, right now we are not really storing
this returned object here anywhere, right?
So if we run this right now,
then this object kind of disappears into nothing.
However, that's easy to fix
because we can simply assign the result
of running this IIFE here to a new variable.
And so let's call this just like before, the shopping cart,
let's say shopping cart two.
And so now, just like before,
we can now say shopping cart two.add to cart,
and here let's say four apples and two pizzas, right?
And so it works.
And indeed, if we take a look at shopping cart,
so shopping cart.
Now, of course, now it's not available here
because we are still inside of a module,
and everything that is in a module
is private to that very module.
And so therefore we can now not access anything
from this module here, here in the console,
because this is your basically the global scope,
but in the global scope, we are not creating any of this,
so not even shopping carts too, right?
So again, that's because it is scoped only to this module,
but let's still take a look edit here in the console,
so of course this we can do.
And so here we see that indeed,
we only exported these four things.
Now, of course our cart now got manipulated,
so that one that we defined here in the function, okay?
But of course, on the other hand,
the properties that we basically wanted to make private,
they are not accessible.
So we cannot do this, right?
So they are undefined.
All right, and that's actually it.
That's the implementation of the module pattern.
Now, do you understand exactly how and why this works?
I mean, how do we, for example,
have access to the cart variable here
and even are able to manipulate it,
so we see that it at changed, indeed.
So how are we able to do that, even if this IIFE here,
so this function has already returned long ago, right?
So this function, of course,
was only executed once in the beginning,
and then all it did was to return this object
and assigned it to this variable, right?
But then we are able to use all of this
and to also manipulate the data
that is inside of this function,
which is the function that returned this object.
And the answer to how all of this works like this
is one more time, closures.
So closures, remember, allow a function to have access
to all the variables that were present
at its birthplace, basically.
So that was a nice analogy that I used back then
when we first talked about closures,
and I think it's a very nice way to visualize
how it works also in this case.
So the add to cart function was created here, right?
And so this function here
is the birthplace of this function.
And so therefore this function never loses connection
to its birthplace, which was this function here.
And so that birthplace, so to say, is all of this scope,
which contains of course the cart.
And so therefore the add to cart function out here
can still access that cart variable
that was in the function, okay?
So the reason why this works
is not because the cart variable is also in this object,
so that's not relevant
because here we are not using this.cart, right?
We are simply using cart.
So here we could also log
something that is indeed private to this module.
So something that will not be in this exported object.
So let's say shipping cost is, and then shipping cost.
And so in order to produce this string here,
the function will also have to use this variable
that was only present at its birthplace,
but which no longer does exist besides that.
So, indeed that still works,
so the function is still able to access
that value of 10, okay?
And for an even deeper explanation of why this works,
you can, of course, always go back and revisit
that lecture about closures.
Okay, but in essence, again,
this is how the module pattern works and it works very well,
and it has been working for a long time for developers,
so long before ES6 modules even existed in JavaScript.
Now, the problem is that if we wanted one module per file,
like we have with ES6 modules,
then we would have to create different scripts
and link all of them in the HTML file.
And that then creates a couple of problems,
like we have to be careful with the order
in which we declare them in HTML,
and we would have all of the variables
living in the global scope,
and finally, we also couldn't bundle them together
using a module bundler.
And so as you learned at the beginning of this section,
using a module bundler is very important
in modern JavaScript.
So the module pattern that we just learned about
does indeed work quite good, but it has some limitations.
And so that's exactly the reason
why native modules were added to the language in ES6.