How do Javascript closures work? Intermediate concepts...
You're familiar with the concepts covered in How do Javascript closures work? A basic introduction...
You have a basic understanding of object oriented programming.
At this point, you should have a low level understanding of Javascript closures. You see how closures can be used to create and freeze new chunks of code with their own local variables, but maybe it's not entirely clear how this is valuable or what this has to do with object oriented programming. Well my friend, this is where the fun part begins!
In this article, we're going to explore how closures and objects can create the data structure and methods to maintain a very basic client side comment thread. Before we get started, I should note that we will not be building an interface for this example, nor will we be discussing any server side aspects, we will only be exploring the underlying javascript data structure and it's associated methods.
In this example, we are going to call our comment thread a wall. In our wall, we will have comments, and those comments will have associated data (upvotes, replies, etc.) Let's create our wall object and some very basic associated methods:
function initiateWall () { var commentThread = []; /* this is an enclosed empty array that will hold the comments on a newly initiated wall object */ return { showComments: function () { /* a method to show comments on the wall */ return commentThread; }, addComment: function (string) { /* a method to add a comment */ if( commentThread.push(string) ){ return true; }else{ return false; } }//end of addComment };//end of return }//end of initiateWall var ourWall = initiateWall(); //we create our new wall object ourWall.addComment('comment 1'); ourWall.addComment('comment 2'); ourWall.addComment('comment 3'); //we've added three comments console.log( ourWall.showComments() ); //the output is an array with our 3 comments //['comment 1','comment 2','comment 3']
Now, the above example is a good start. We can initiate a wall object, add comments to it and access the array of comments in it's entirety. But what if we want to access a specific comment or it's properties? In this case, an array isn't the optimal data structure for the job. We're going to change commentThread to an object, so that we can use key : value pairs to easily access a comment using a hashed unique identifier (the unique hash may come from the server, a hashing function, etc. that's entirely up to you, but for this example, I'm simply going to make the hashes up) Let's take a look at our updated function:
function initiateWall () { var commentThread = {}; /* we are now initializing our comment list as an object itself within the wall object. We do this so that we can reference keys within the object, instead of indexes within an array */ return { // // showComments: function () { return commentThread; }, // // addComment: function (hash, string) { if( !commentThread[hash] ){ /* make sure the comment hash doesn't exist already */ /* let's initialize our new comment object and give it some basic properties */ commentThread[hash] = {}; commentThread[hash].comment = string; commentThread[hash].upvotes = 0; commentThread[hash].replies = []; return true; }else{ return false; } }, // // showSingleComment: function(hash){ /* We've added a new method here that will give us access to the properties of a single comment within the commentThread by passing it the comment's unique hash */ return commentThread[hash]; } // // };//end of return }//end of initiateWall() definition var ourWall = initiateWall(); /* now, we'll pass in some fabricated hashes and coinciding comments */ ourWall.addComment( 'xh7w3' , 'comment 1'); ourWall.addComment( 'yd5b1' , 'comment 2'); ourWall.addComment( 'nd9p6' , 'comment 3'); console.log( ourWall.showComments() ); /* displays list which contains all of the comments in our wall object */ console.log( ourWall.showSingleComment('yd5b1') ); /* displays a single comment, referenced by the unique hash we've assigned it */
Alright sweet! We've got a pretty cool working example of how we can use a Javascript closure to initiate a wall/comment section. As you may be able to see, we can continue to add methods to our object to increase the complexity of our program. For example, I could add the following to initiateWall () to handle replies and upvotes:
// // addReply: function (hash, string) { if( commentThread[hash] ){ /* make sure the comment hash exists */ commentThread[hash].replies.push(string); /* and add the reply to our array of replies */ return true; }else{ console.log('Comment with hash '+hash+' does not exist!'); return false; } }, // // addUpvote: function (hash) { if( commentThread[hash] ){ /* make sure the comment hash exists */ commentThread[hash].upvotes++; /* and increase the upvote count */ return true; }else{ console.log('Comment with hash '+hash+' does not exist!'); return false; } } // //
We've come a long way from How do Javascript closures work? A basic introduction...! If you're not quite grasping these concepts, I highly encourage you to copy the example above and modify it on your local machine or a JS Bin. Try instatiating a wall object using initiateWall(), and mess around with invoking it's methods. As an outro example, I'll add some final code below, and the ouput that it will give you:
var ourWall = initiateWall(); /* We'll pass in our fabricated hashes and coinciding comments */ ourWall.addComment( 'xh7w3' , 'comment 1'); ourWall.addComment( 'yd5b1' , 'comment 2'); ourWall.addComment( 'nd9p6' , 'comment 3'); console.log( ourWall.showComments() ); /* The output is as follows: [object Object] { nd9p6: [object Object] { comment: "comment 3", replies: [], upvotes: 0 }, xh7w3: [object Object] { comment: "comment 1", replies: [], upvotes: 0 }, yd5b1: [object Object] { comment: "comment 2", replies: [], upvotes: 0 } } */ /* let's add a couple upvotes and a couple replies to this comment */ ourWall.addUpvote('yd5b1'); ourWall.addUpvote('yd5b1'); ourWall.addReply('yd5b1','Great comment!'); ourWall.addReply('yd5b1','Very average comment!'); /* now let's see what the 'yd5b1' comment object looks like within ourWall */ console.log( ourWall.showSingleComment('yd5b1') ); /* The output is as follows: [object Object] { comment: "comment 2", replies: ["Great comment!", "Very average comment!"], upvotes: 2 } */
I hope this example has been of use to you, and as always, if you have any questions please don't hesitate to ask! You can leave a comment in the comment section below and I will do my best to help clarify. Happy coding and enjoy the journey!