Pre-ES6 Class Patterns0:00
Before ES6, JavaScript classes didn't really exist in the traditional sense. In order to create a class, we had to do something kind of hacky. And I'll show that here. So let's get rid of everything that we have now. And this method used a combination of functions and the knowledge that basically everything in JavaScript is an object. So let's say we had a program like a grocery list, where we had two classes. One is an Item describing a product, and the other is a PurchasedItem that inherits that Item's class, but adds on some additional methods and properties to it. And this is what we would have to do.
item's class, but adds on some additional methods and properties to it. And this is what we would have to do. I'm not going to spend too much time on this, since this isn't really modern JavaScript, but I'd like to give you an overview of how object-oriented structure was added into JavaScript before ES6 classes. So first we started with a function generated like a class. Now then in order to have a method attached to that item that does something, we use JavaScript's prototype attribute. Because most things in JavaScript, functions included, are objects, prototype is a way to chain on attributes from other objects onto new ones, and also to create new attributes.
Inheritance with call()1:44
everything from the main Item class? Well down here we have a purchaseItem function. And this takes in a name, category, and price. Now we could copy everything and set this name as name and this category as category, but that kind of defeats the purpose of inheritance in OOP. So instead we use this call method, which when used on a function allows us to call that function, but specify a specific context as the first attribute. We're passing $this in, which associates with the purchaseItem function. So when $item is called and this name is set to name, it's not calling this on $this item, it's calling this on purchaseItem.
So when item is called and this name is set to name, it's not calling this on this item, it's calling this on purchaseItem. So now we have the name and category associated with purchaseItem, and the only thing missing is the price. And then down here, like before, we set a method on that purchaseItem getDetailsWithPrice, which returns the name, the category, and the price of that item. In here though is where things start to get a little weird. So by creating this function purchaseItem, we have all of the properties set up of the original item, but we have none of the methods. Now we could just copy and paste them, but again that defeats the purpose of inheritance.
Prototype Chain Setup2:52
original item, but we have none of the methods. Now we could just copy and paste them, but again that defeats the purpose of inheritance. So what we do is we set the prototype of the purchaseItem function to the same prototype as the item using Object.create. That way we pass in the property, like getDetails, onto the purchaseItem function. That though unfortunately resets the constructor of the class to item, which every object has a constructor. So in order to reset that, we manually set the constructor to the purchaseItem function. It's definitely complicated, but then to use it, it looks like this. So let's say we create a new item.
It's definitely complicated, but then to use it, it looks like this. So let's say we create a new Item. And it'll be a coffee, and it's under the category of food. But we can then change that category by calling the category property on the item. And we can set that to something like drinks. And then we could create a new PurchaseItem using the PurchaseItem class. And pass in sugar as the name, food as the category, and $2.49 as the price. And because we inherited this from the original Item class, we can call PurchaseItem::getDetails(), which will return just the name and the category of the PurchaseItem. So we can see that working by putting this into the output element in our browser.
which will return just the name and the category of the purchaseItem. So we can see that working by putting this into the output element in our browser. So going back to our browser, we see that we have sugar and food. And if we wanted to use the method specific to purchaseItem, we could specify getDetails with price. And then we get the price of the item as well. So again, this is how classes worked before ES6. But now we have a much more streamlined way of doing things. So let's comment out everything that we have so far. And let's say we wanted to create the same functionality, but with ES6 classes.
Building ES6 Classes4:49
So let's comment out everything that we have so far. And let's say we wanted to create the same functionality, but with ES6 classes. So it starts very similar to a lot of programming languages with the class keyword. So we have class Item, and we have a constructor for that. That takes a variety of properties we want to set on initialization. For our example, that was a name and a category. Like before, we're using the this context and setting the name to this.name and the category to this.category. And to create methods on this Item class, we just stay in the same block as the class and create a new function.
And to create methods on this Item class, we just stay in the same block as the class and create a new function. So getDetails returns this name and this category. And that's our class. Now to extend off of this into a subclass, let's create a new class called PurchasedItem. And all we have to do is say extends Item. So now we've inherited the properties of the Item class, but we still need to do some initialization with the constructor. Because there's other properties in here besides just name and category. Remember we're also needing a price.
Because there's other properties in here besides just name and category. Remember we're also needing a price. So we can set the price as this price is equal to price. And again, we could specify this name is equal to name and this category is equal to category, but that defeats the purpose of this pattern. ES6 classes have a helper method though called super. What super does is it calls the constructor of the class that we're extending off of. But it sets the context of the current item. So by passing in name and category into super, it's calling the constructor of the item property here, but using the same context as this purchasedItem.
So by passing in name and category into super, it's calling the constructor of the item property here, but using the same context as this purchasedItem. So by just calling super with name and category, it's a shortcut for this name is equal to name and this category is equal to category. And like before, let's create a method just for this class: getDetailsWithPrice. And we'll return this name, this category, and this price. And to use it, we do the exact same thing that we did earlier. Except instead of using var, I'm going to use let. So let item is equal to a new item(coffee, food).
Except instead of using var, I'm going to use let. So let item is equal to a new Item(), coffee, food. But actually item's category should be drinks. And let's create a purchasedItem as a new PurchasedItem(), sugar, food, and $2.49. So now going back into our browser and refreshing, we see the exact same thing as before. Oh, I also didn't realize that I didn't show that the item's category was changed after setting item category. And call the getDetails method. We can see that it is now coffee and set to drinks. These classes also have static properties and methods that we can add to them as well.
Static Methods and Properties8:03
We can see that it is now coffee and set to drinks. These classes also have static properties and methods that we can add to them as well. So in the main Item class, we could call a static property by using the static keyword and providing it a name, like something like maxItems, and then set that to a value. We can also create a static method by using the static keyword and passing in a function name. Both of these items do not need to have a class initialized before they can be used. So instead of outputting item::getDetails, we can call Item, not the initialized class. And actually, let's comment out all of this first so that we don't get them confused.
class. And actually, let's comment out all of this first so that we don't get them confused. And in the browser, we can see that it shows up as expected. And if we wanted to call the static property that we set, it's just item maxItems. We can also reference the extended Item class static properties using super, just like we did in the constructor. So if we had a static method in this PurchasedItem class, let's call it getNumberOfItems. And we'll return 3 out of the max number of items possible. Well we have that property up here in the Item class as 10 under maxItems. So in our getNumberOfItems method, we can just call super maxItems.
Well we have that property up here in the Item class as 10 under maxItems. So in our getNumberOfItems method, we can just call super maxItems. So super in this case is going to be using the Item class and get the maxItems property from that. So now down here, instead of Item maxItems, we can call purchasedItem getNumberOfItems. And then in the browser, we see we have 3 out of the 10 that we set. Alright I think that's about it for classes. In the next episode, we're going to dive into the interesting world of promises.
