Need Multiple Game Instances0:00
Creating objects is relatively simple. I mean, we've been doing it for the past few episodes to where we call, well, we don't really call anything. We have this immediately invoked function that returns an object, and then we can use that game object to play our game. But I want to be able to create multiple game objects so that we could have something like an easy game. And then we could have a factory function called createGame, and then we could pass in the options that we want, like maxAttempts could be 10.
and then we could pass in the options that we want, like maxAttempts could be 10. That way we would have a game that's bound to the options of a range of 1 to 10, and then it has a maxAttempts of 10. I can't really think of anything easier. I can think of something easier, but that's very easy. But then I would want to be able to create a hard game so that the maxRange would be like 100, and then we would have maxAttempts as 10. That's slightly more difficult.
and then we would have maxAttempts as 10. That's slightly more difficult. And then we could have a really hard game so that the range would be the same thing, but we would have like five attempts or something like that. So the idea being that we are creating multiple objects that represent the same thing, a Game, but they are bound to whatever options that we pass to them. That way we can play a lot of easy games or a lot of hard games or a lot of really hard games.
Refactoring into Factory Function1:16
That way we can play a lot of easy games or a lot of hard games or a lot of really hard games. And as far as a factory is concerned, we're almost there. We have this immediately invoked function that is returning an object that has that play method. So all we really need to do is change this so that we would have a function called createGame. And yeah, other than some syntax things, such as down here at the bottom, we aren't executing this off at the very start.
such as down here at the bottom, we aren't executing this off at the very start. We just have this function that we want to call. We need to lift our options object from the play method to the createGame function. And there we go. The only thing about this is that now createGame is globally accessible. So we want to protect this as much as possible, which means that we could do something like this.
So we want to protect this as much as possible, which means that we could do something like this. We could say const createGame equals this function. And there we go. We have protected that so that now we can create a game object bound to whatever objects we pass to it. And there we go. We have our object. So I'm going to call this factory function because that is exactly what it is.
Using Closures for Encapsulation2:24
So I'm going to call this factory function because that is exactly what it is. But let's talk about this a little bit. So we have this factory function and these values, minRange, maxRange, and maxAttempts are all accessible inside of this createGame function. So we are essentially creating a closure that closes around those values. So this means that the play method for our object has access to those values and everything's fine.
So this means that the play method for our object has access to those values and everything's fine. This is actually ideal because these values are protected. They're not accessible, or at least they're not easily accessible outside of our object. And we don't have to reference them. They are still in memory because we have created a closure around them, but we don't have to create properties.
because we have created a closure around them, but we don't have to create properties to represent these values like this. We would have minRange is minRange, maxRange is maxRange, and then maxAttempts is maxAttempts. I mean, we could take this approach. There's nothing wrong with this because these are values that we would still need to have access to,
because these are values that we would still need to have access to, but we already do have access. So there's no need. One thing to note though, whenever you are creating an object using object literal notation, like we are doing here, and you have a property name that is going to be assigned the value of a variable.
and you have a property name that is going to be assigned the value of a variable that has the same name, we can shorten this so that it's just minRange and maxRange and maxAttempts. So saves quite a bit of typing there, but we don't need to do that because we already have access to these things. They're going to be kept in memory. And there we go.
Factory Method Memory Costs4:06
They're going to be kept in memory. And there we go. But there's also something to talk about here because we are creating individual objects every time that we call create game. And each one of those objects has a play method. Now, what is the method? It is a function. It is a function that is assigned to a property. And what are functions?
It is a function that is assigned to a property. And what are functions? Well, they're objects. So every time that we define or we create a function, we are essentially creating an object and that object has to take up memory. So every time that we call createGame, we are creating an object that then has another object representing a function for the play method. And the implications of that is this,
representing a function for the play method. And the implications of that is this, we end up with three separate objects representing the play method on each one of these objects. So if we see if the play method on easyGame is the same as the play method on the hardGame, well, I'll go ahead and tell you that it's going to be false because we have created new function objects for the play method on each one of these game objects. And that's not a big deal in this particular case,
for the play method on each one of these game objects. And that's not a big deal in this particular case, but just imagine an application where you need to create hundreds, maybe thousands of objects. You want to be as memory efficient as possible because also too, think about what a function is. It's essentially static. It's not dynamic. We don't change the contents dynamically of a function.
It's not dynamic. We don't change the contents dynamically of a function. A function executes the same way. The data that we provide the function might be different here and there, but the function itself is the same. So instead of using a factory whenever you need to create an object that has methods, I think it's better to create a class. So let's do that.
Switching to a Class6:01
I think it's better to create a class. So let's do that. By using class and then the name of the class, and then we have a constructor for that class. So it's a lot like creating a class in php. The syntax is going to be a little bit different because the constructor for a class in JavaScript is constructor. So you have to write that whole thing out. And then inside a constructor, we can define our properties.
So you have to write that whole thing out. And then inside a constructor, we can define our properties because now we aren't creating a closure or anything like that. We are essentially going to new up this constructor and we are going to provide the min, max and maxAttempts. Oops, I don't need to cut that. So we aren't enclosing anything. These are values that we need to make accessible throughout the rest of the objects.
These are values that we need to make accessible throughout the rest of the objects. So that means we need to create a minRange property equal to whatever was supplied. Then we will do the same thing for maxRange. And we do this by using this, this->propertyName, and then equals the value. this->maxRange equals maxRange. this->maxAttempts equals maxAttempts. And as I mentioned before, this is a lot like this.
This dot max attempts equals max attempts. And as I mentioned before, this is a lot like this. That's a little confusing. The this keyword or the this variable in JavaScript is a lot like the this variable in PHP in that it references this object, this object that we are currently working in. The problem here though, is that by creating these properties, they are public. So there's a lot of differences.
is that by creating these properties, they are public. So there's a lot of differences between classes in php and JavaScript. For one, we can't create a constant property. That's just not how we can do it. I wish we could, that would be really nice. We can, however, create private properties and we prepend those by a pound sign. So this is going to look somewhat like php in that we have to define our private variables.
So this is going to look somewhat like php in that we have to define our private variables or our private fields outside of the constructor. So we will have minRange, maxRange, and maxAttempts. And if we wanted to supply a default value here, we could, but we don't necessarily need to do that because the constructor is going to do that. So inside of this class, anytime that we want to reference these private fields, we still have to use this,
anytime that we want to reference these private fields, we still have to use this, but we then use the property name of $minRange, $maxRange, $maxAttempts. So these are private. Let's just go ahead and mark those with a comment there. So we have private fields, but we have to declare those as private before we can use them inside of the constructor or anywhere else within the class.
before we can use them inside of the constructor or anywhere else within the class. So if I take out that minRange, we can see, oh, there's a problem there. We have private, but now we need our play method. So I'm just going to copy this because for the most part, that's all we need to do is copy it, but we do need to go through, and anytime that we reference any one of these properties, we now need to prepend it with this,
and anytime that we reference any one of these properties, we now need to prepend it with this, and then the pound sign. So we would have this->maxRange, this->minRange, this->minRange, history. I think we could leave inside of the play method so that we don't have to do anything there. The guess is less than minRange, greater than maxRange. guess is all local. I think everything else is local.
Guess is all local. I think everything else is local. Of course, I've probably missed something, in which case we will find a problem. So this is our class. Although it looks like the coloring of maxAttempts is different. So, oh, nope, right there, maxAttempts. All right, thank you, Visual Studio Code, because that, I would not have picked that up.
All right, thank you, Visual Studio Code, because that, I would not have picked that up. So to create a class, it's a lot like php. class, and then the name. If you want private fields, you have to declare those inside of the class body. Then you define your constructor, then your methods. So now let's scroll all the way down to where we create these things, and let's do this. I'm gonna copy those, and we're gonna have easyGameTwo,
Instantiating and Sharing Methods10:11
to where we create these things, and let's do this. I'm gonna copy those, and we're gonna have easyGameTwo, hardGameTwo, and reallyHardGameTwo, except that we will new up the Game constructor, pass in the options that we want, and then let's check if easyGameTwo.play is the same as hardGameTwo.play. I'll go ahead and tell you that it is true. So now this means that we can create game objects using our Game class.
So now this means that we can create game objects using our Game class. And while, of course, each one of those objects is going to consume their own block of memory, the functions or the methods that we define in that class are shared between all of the objects of that class. And that's really what we want. Now, of course, there are many different patterns for creating objects, but the two primary ways are the factory function and using classes.
for creating objects, but the two primary ways are the factory function and using classes. Now, factories are really useful for creating objects that, of course, have a similar structure, but they are primarily data-related, meaning that there's no methods or anything like that. It's just a way of structuring data. If you need to create multiple objects that have methods, a class is typically the better approach, especially from a memory efficiency standpoint.
a class is typically the better approach, especially from a memory efficiency standpoint.
