Check out a free preview of the full A Tour of JavaScript & React Patterns course
The "Singleton Pattern" Lesson is part of the full, A Tour of JavaScript & React Patterns course featured in this preview video. Here's what you'd learn in this lesson:
Lydia explains the singleton pattern restricts the instantiation of a class or object to one instance. The instance is unmodifiable and can be accessed globally throughout the application.
Transcript from the "Singleton Pattern" Lesson
[00:00:00]
>> Okay, so next is the singleton pattern. And a singleton is basically a single instance that can be shared throughout the entire application. Normally when we have classes, maybe like this Counter class, we can either export it as a class and then instantiate a new counter whenever we import it in a file, but we usually don't want this.
[00:00:21]
So with a singleton what we can do is ensure that there's only that single instance that can be used throughout our application. So here, for example, we have file one, I should just make this big, we can have file one that imports the counter, the singleton, it increments it.
[00:00:38]
You can see the increment, or count, going up, and it continues to do that when we increment it in other files as well. Because they all share that single instance. So they all have that reference to the same class, to the same instance, in the application. Now, in ES6, there are several ways to create singletons.
[00:00:57]
The very first one is using a class. And there are a couple of things here that are very unique to singletons. So the first thing is that when we are instantiating the new counter, or in this case to Counter class, we have to make sure that an instance doesn't already exist.
[00:01:14]
Because that's the thing of the singletons, only one instance should ever exist. So if we are instantiating a new one and we do see that an instance exists, we have the instance variable here that we set equal to the new instance when we instantiate it. We will throw an error, like hey, you cannot do that, this is not a singleton, you can only create one instance.
[00:01:34]
Then another thing about singletons is that they shouldn't be modifiable, so no file or no part of the application should be able to change it, it's just that class, that's it. And we can do that in JavaScript with the object.freeze. That makes sure that we cannot add any properties or change the properties that currently exist on an object or a class, cuz classes are, of course, objects.
[00:01:59]
And we actually instantiate the new counter, so this new class, as a singleton, already in this file before we export it. So whenever files import it, they import the instance and not the class. So they cannot instantiate the already instantiated instance, if that makes sense. So yeah, we only instantiate it once, freeze it to make sure it's not modifiable.
[00:02:20]
However, in a lot of cases, we don't actually have to use classes anymore, in ES6 we can actually just use objects. Now if we have the counter example again, we can see that we can create a counter object that just has the getCount method, increment and decrement method, we can freeze it and export it.
[00:02:39]
I always like to keep it pretty short, we can just instantly freeze the object that we create, and then export it. So whenever other files import it, they just have that one single object. Now some of the trade of singletons is that, of course, it's really good for memory, if you care about that.
[00:03:00]
Because we make sure that we can only have just one single instance instead of having to create memory or use memory for every one of them. But there actually are also a lot of cons to the singleton pattern, especially in 2022, what it is now. Now like I said, these 15 modules are singletons by default.
[00:03:19]
So we no longer really have to think too much about, okay, creating this or achieving this global, non-modifiable behavior, because modules by default are already singletons. Then there's also stuff like dependency hiding. Maybe a singleton is importing another module that we don't really know about that. So every time that we import it, we could be modifying this singleton without really knowing it, this is dependency hiding.
[00:03:48]
Then there's also the testing, it's really difficult to test singletons because we change it globally. So every one, small modification can lead to the entire test suite failing, which is highly, highly annoying. And we also have to reset it every time. So that's definitely a big con to singletons.
[00:04:06]
But yeah, long story short, singletons are great if you wanna achieve some kind of global state in your application. But maybe consider using a module if that works for your application as well.
>> What does the default keyword do to an export?
>> The default, so it kind of does two things.
[00:04:27]
I'll just show you. I was gonna fork it so I didn't change the, so let's say I was going to default connection here. And I go to test.js. I can just do, import connection from './index.js'. And with a default import, I don't have to use this in brackets.
[00:04:55]
I could have actually named this anything. It could have been like, databaseConnectionlol, and it still would have imported this default export. Now, normally, if we have maybe a named export, so anything within brackets, or export connection, in that case, I do have to explicitly say this is named connection.
[00:05:12]
Now I can still rename it to be databaseConnection with, I always forget the name of this thing, as, sorry, with the as keyword, but that's the biggest difference. Now actually, also the default keyword. So what we're exporting here is more like, okay, wait, let me just create a comment.
[00:05:28]
I'm so bad at live coding, it's hilarious. Okay, we can actually have default connection, and then we also have connection here. So when we're importing the default, I could also just say default, which is importing the default connection. Okay, I don't know if that explanation was any better.
[00:05:51]
The biggest advantage of using a default export is that we don't have to think about the name with which we're exporting the value. I could have just done databaseConnection. And if I maybe in this file already had something called databaseConnection, I don't know, just a private value here, then this would have resulted in a naming collision.
[00:06:12]
So I don't want to use that. So I either don't want to rename this import or the default export, which is most likely the case cuz this can be anything. I can just rename it and make sure that I'm using that throughout the application, or at least in this file.
[00:06:26]
So that's the biggest difference, is just the way that we can import them in other modules. Again, there's a small exercise. So this is a fake database connection class, and we want to make sure that there's only ever one single database connection. This is actually a pretty common use case for a singleton because we don't wanna just have a hundred database connections within one application.
[00:06:49]
So you can see the class here, DBConnection, we have a connect and disconnect method. And we want to make sure that only one instance can ever exist. So make sure that this is a singleton, non-modifiable, and a single instance.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops