مرور بازنویسی اپ Todo0:26
It makes use of object literals to house all of your code and makes heavy use of methods to break everything up into logical chunks. So let's refactor our to-do app and see how we can do this. So if we go into our code, we actually don't have too many lines of code here. We have from line 39 to line 92, so about 50 lines of code. And again, if this were your entire application, then this is totally fine. And in fact, if you take a look at the JavaScript that I write, especially within a server-side application like Laravel, it's very similar to this. So I would <script> tag before the closing </body>. A lot of times I'm making use of a third-party library, so I import that from a CDN, and
Remove Global Scope1:21
So let's go ahead and do that here. So again, one quick thing you might do to refactor your code is remove this from the global scope. So everything in here right now is within the global scope. For example, this variable, todoFormInput, is within the global scope, which isn't a big deal if your variable names don't collide with global variables within the browser. But if you want, you can wrap this in an iffy, an immediately invocable function expression, to remove it from the global scope. So again, if we go into the browser here, and open up DevTools, if I paste in that variable, you'll see it's available because it's within the global scope.
So again, if we go into the browser here, and open up DevTools, if I paste in that variable, you'll see it's available because it's within the global scope. But if I wrap this in an iffy, so I have a snipper for that, iffy, I-F-F-E, or I-I-F-E, and grab all this code and put it within there, so all of this, up to here, let's cut that out, let's paste that in, let's re-indent that. Now it's not within the global scope. So if I save that, our app should still work. Add. But now our variable names are not scoped globally. So if I paste in that variable name, you can see todoFormInput is not defined, because
Create Object Literal2:24
But now our variable names are not scoped globally. So if I paste in that variable name, you can see toDoFormInput is not defined, because it's no longer on the global scope. But now let's actually refactor our code to make use of object literals. So back to our code. And if you want, you can also put this within iffy, but I'll just put it on the global scope. And we're only going to have one global variable, which is pretty unlikely to clash with any variable names in the browser. So let's make an object literal here. Let's name it const toDoApp equals an object literal.
So let's make an object literal here. Let's name it const todoApp equals an object literal. Okay. And the idea here is to put everything within this object. So we'll have different methods and properties on this object, which correspond to what we're doing for our application. So for example, for this block of code here, this is selecting DOM elements. And we can have a method dedicated for that specifically. And then down here, we're calling this method. And then after that, we have our event listeners.
And then down here, we're calling this method. And then after that, we have our event listeners. So listening for events. So let's see if we can do that. So how about we make a method on this object literal that initializes the app, let's call it init for initialize. It's a function. And let's just console.log init, the to do app. Okay. So now outside of this object, we can call the init method.
Okay. So now outside of this object, we can call the init method. So to do app, again, this is our only global variable init. Okay. And this should work back to the browser, we can see in it the to do app. So now we can sort of delegate the different tasks for the application in order for it to run correctly. So like I said, we want to select the DOM elements. So that will be our first step. Actually, before I do that, we actually have the short form syntax we can use.
Cache DOM Elements4:13
So that will be our first step. Actually, before I do that, we actually have the short form syntax we can use. So instead of init(), we can just say init and the brackets, and that would be the same thing. So how about we have a dedicated method to select the DOM elements. So let's do that, say this, because now we're in the context of an object. So we have to use this to call other methods. So this.selectDOMElements, or another word for selecting DOM elements is caching DOM elements. So how about we name it that cacheDOMElements, okay.
is caching DOM elements. So how about we name it that cacheDOMElements, okay. And we can have a method dedicated for caching DOM elements. So cacheDOMElements, okay, this is a method as well. And we can move all of this code into there. But we have to make one small change. So let me grab all of this. Let's paste that into here. Let me just reinvent this. But now we want all of these elements to be properties of this toDoApp object.
Let me just reinvent this. But now we want all of these elements to be properties of this toDoApp object. So essentially, what we want is, for example, a toDoForm property. And we can do like this as well. So if we add a property in toDoForm, and then the value of that would be document.querySelector('toDoForm') like it is here. This would work as well. And then anytime we need to refer to the toDoForm, we can reference it using this.toDoForm. So again, this would work, but I want to do it all within the cacheDOMElements method.
to do form. So again, this would work, but I want to do it all within the cacheDOM elements method. So to do that, we can just say this in front of all of these variables. So like this, this.toDoForm here, this should work. So if I save that back to the browser, you can see we get the toDoForm here. Okay. Let's get rid of this. And after we select the DOM elements, what are we doing after that? If I scroll up here, you can see we're calling calculateItemsRemaining.
Calculate Items Remaining6:08
And after we select the DOM elements, what are we doing after that? If I scroll up here, you can see we're calling calculateItemsRemaining. So how about we call that method and move the logic for that method into our object. So back here, let's define that method, calculateItemsRemaining. And we have to call it as well. So right after caching the DOM elements, let's say this.calculateItemsRemaining. Okay. And we can grab the logic for this method from here, or did I put it right here? So all of this can go within our new method within that object.
And we can grab the logic for this method from here, or did I put it right here? So all of this can go within our new method within that object. So back here, let's paste that in. Let me get rid of this comment here since we're no longer using it. And the only change we have to make here is itemsRemaining should now be this. itemsRemaining. Okay. itemsCount is making use of toDoItemLabels, which is a local variable here. So this is fine. And if you want, we can actually just inline this entire thing over here.
Refactor Event Listeners7:15
So this is fine. And if you want, we can actually just inline this entire thing over here. Let's go ahead and do that since we're just referring to it in one spot. So I'm going to paste that in and get rid of this. Okay. Now, what are we doing after calculating itemsRemaining? So after this, we are listening for events. So how about we define a method named listeningForEvents and then call that. So let's duplicate this. listenForEvents.
So let's duplicate this. Listen for events. Let's add that here. Listen for events. Okay. And let's start with adding toDo. So if I scroll down here, let's look for the event that submits the form, which is adding our toDos. So all of this here. So let's grab this.
So all of this here. So let's grab this. Let's paste that within. Listen for events in our object. Paste that in. We have to reinvent this and we have to add this to the variables we're referring to. So in this case, this toDoForm, these are local to the function. So this is fine. You have to call this on this variable as well. And I believe for this one as well.
You have to call this on this variable as well. And I believe for this one as well. Oh, and also the method underneath. So this calculateItemsRemaining. So now if I save this, let's actually comment out all of our previous code and let's see if adding actually does work. So all of this I'm going to get rid of or at least comment out for now. So this and all of this, let's comment out. So now we're just left with our object and you can see how this is much more organized. For example, we just have our entire object.
So now we're just left with our object and you can see how this is much more organized. For example, we just have our entire object. We have this init method which kicks everything off and we are calling that outside of the object here. So down here to do app.init and within the init function, we are just doing the different steps in order for the app to work correctly. So we're caching the DOM elements or selecting the DOM elements. We're calculating the itemsRemaining and then we're listening for events. So again, much more organized than what we had before. So let's see if this actually works.
So again, much more organized than what we had before. So let's see if this actually works. So let me save this back to our browser and let's try adding, see if we get any errors or if it actually works. To doItem is not defined. Where's doItem here? Oh yes, this has to be this as well. Okay. I believe this one has to be this as well toDoFormInput. So let's say this toDoFormInput value toDoItem label is local.
I believe this one has to be this as well to do form input. So let's say this to do form input value to do item label is local. So that's fine. And so is newToDoItem. So that should be fine as well. Save that. Let's try it one more time in the browser, add, and it does work. Awesome. Go back to our code. If we want, we can even extract the code that adds the to do.
Go back to our code. If we want, we can even extract the code that adds the toDo. So all of this within this callback function into its own dedicated method. So let's grab all of this within the callback method. Let's cut that out and let's define a dedicated method. So I'm going to remove this. Let's name that method addToDo. So this.addToDo, and we can add it right underneath, addToDo. That takes in an event. Okay.
That takes in an event. Okay. Let's paste in that code. Let me just re-indent this, and this is not going to work, but let's see why. Let me save this back to the browser here. Let's try adding, and we get cannot read properties of undefined cloneNode. So to me, that looks like this to doItem is null. So how about we console.log this here so we can see what the value of this is and what we expect it to be is the entire toDoApp object. And actually I'm going to console.log this as well right here.
we expect it to be is the entire toDo app object. And actually I'm going to console.log this as well right here. So console.log this, and this should happen automatically before we even add toDo. So let me save this. So back to the browser. You can see the console.log for this within here, this one right here, is the toDo app object. So you can see our functions here, init, cache, DOM elements, and so on. So it is the toDo app object. But now when I add toDo, let's see what the value of this is.
So it is the toDoApp object. But now when I add toDo, let's see what the value of this is. So add, and you can see now the value of this changed, and it's now the form, which we don't want. So one quick fix for this would be to just refer to this using the object toDoApp. But I still want to use the this keyword. So what we can do here is use the bind method. So we can say bind(this). And what that's going to do is within this method, this should now refer to the toDo object, which is what we want.
And what that's going to do is within this method, this should now refer to the toDo object, which is what we want. So now if I save this, it should work. So back here, let's try adding. You can see this is the toDo object. And if I add, this is also the toDo object down here. And it does work. Cool. So let's get rid of those console logs. And you can see this is much cleaner.
So let's get rid of those console logs. And you can see this is much cleaner. Let's add our other event listener. So down here, where's the other one? So this is for add. And the other one was for, I think it's underneath calculateItemsRemaining. So the other one's right here, and it had event delegation. And depending on where we clicked, we either checked the toDo, so this case, or we deleted the toDo, so this case.
Okay. So let's add this in front of toDoList. And it looks like we actually needed the event here, but it passed it in automatically. So let's say event, we're going to get rid of that anyways, and put it into its own dedicated method. And now we just have to add this on this method here. And I'm saying this a lot, but I hope it makes sense. This thought calculateItemsRemaining. Okay. So let's see if deleting and checking toDo's still works.
Okay. So let's see if deleting and checking to to dos still works. So save that back to our browser. Let's add, let's delete, and let's check, and it does work. But again, let's move this logic to a dedicated method to make it a bit cleaner. So let's grab all of this logic here. Let's define its own method. We don't need this. We just have to make sure to pass in the event. So this, let's name it, checkOrDeleteToDo, because that's what we're doing.
We just have to make sure to pass in the event. So this, let's name it, checkOrDeleteToDo, because that's what we're doing. And let's do the same thing here and say, bind this. Okay. So let's define that method right here. checkOrDeleteToDo. It takes in the event. Okay. Let's paste in what I have in my clipboard. Let me re-indent.
Let's paste in what I have in my clipboard. Let me re-indent. And this should still work, hopefully. Save that back to the browser. Let's add, let's delete, let's check. Everything still works. But if you want, you can even take this a step further. Usually when you have a method name, like check or delete to do, the name itself implies that we're doing more than one thing. So you might want to break that down into multiple methods as well.
that we're doing more than one thing. So you might want to break that down into multiple methods as well. I think this is fine since it's not much code. We're just checking what we clicked on and reacting accordingly. So this is the case when we're checking the to do, and this is the case when we're deleting the to do. But just so we have extra practice for extracting methods, let's go ahead and do that. For the case when we're checking the to do, let me grab this logic here. Let's cut it out. Let's define a method dedicated to just checking the to do.
Let's cut it out. Let's define a method dedicated to just checking the toDo. So this checkToDo, and we want to pass in the element and the element is event.target. Or you can pass in the entire event as well, but I'll just pass in the element that we clicked on. So that should be fine. Let's define checkToDo down here. checkToDo. That takes in an element.
Check to do. That takes in an element. Okay, let's paste in our logic. Let me just reinvent this. And instead of event.target, it's just the element. Okay. And pretty much the same thing for delete. So let me grab this logic. This.delete to do event.target. Let's define that method right here.
This dot delete to do event.target. Let's define that method right here. Delete to do element. Let's paste in our logic. Let's change this to element. And hopefully I did everything correctly and checking and deleting still works. So let me save that back to the browser. Let's add. Let's delete. Still works.
Let's delete. Still works. Check. Still works as well. So yeah, that's one possible refactor we can do for our to do app, which I feel gives it more structure and organization. So if we take a look at what we have now, we have one object literal here, which houses all of our code. We have this init function, which just delegates to other functions that perform a certain task to get the app working correctly.
We have this init function, which just delegates to other functions that perform a certain task to get the app working correctly. And I just realized I did not create a new file for this refactor. I wanted to do that at the beginning, but I forgot. So let me create a new file. Let's say index.refactor.html. Let's grab everything we have right here. Let's paste that into here. So this is our refactor. We can get rid of all of our commented code here.
So this is our refactor. We can get rid of all of our commented code here. Okay. Let's save this and we can undo all of this and bring it back to what we had before the refactor. So let me just Ctrl + Z all the way to what we had before. Okay. So this is what we had before. It should still work in the browser. Adding works, checking works, items remaining works and delete works.
It should still work in the browser. Adding works, checking works, items remaining works and delete works. Okay. And let's also make sure our refactor works as well. So now I'm going to live server this new refactor just to make sure I didn't screw anything up. Adding items remaining, checking and deleting still works. So now if we compare the two, our refactor is within this object literal and just a bunch of methods that have a dedicated task to perform. Again, I think this is more structured and more organized as opposed to what we had before.
Discuss Other Patterns18:06
of methods that have a dedicated task to perform. Again, I think this is more structured and more organized as opposed to what we had before. And there is some structure to this. Again, we're selecting DOM elements here and then we're adding our event listeners here. But as this app grows, I think this is much less maintainable. Now there are a bunch of other popular design patterns you can make use of to structure your code. For example, there's one called the revealing module pattern, which introduces private and public properties. Or you can make use of classes in JavaScript and leverage object oriented programming,
public properties. Or you can make use of classes in JavaScript and leverage object oriented programming, which is an extremely popular way of structuring your code in other languages like Java and php. As you gain more experience, you'll get a better sense of how to structure your applications. Until then, just try your best to keep it as organized as possible. And don't be afraid to refactor if you feel your code is starting to get more messy and starting to feel less maintainable.
