JS Advanced Objects

https://www.dofactory.com/javascript/advanced-objects

JS Advanced Objects

JavaScript and namespaces

Unlike many other languages, JavaScript does not support namespaces. Namespaces are important because they help reduce the number of identifiers for variables, objects, and functions that are added to the global scope in your application. JavaScript is a flexible language and there are ways to work around this limitation and implement your own namespaces. First, let's explore the need for namespaces a bit more.

In JavaScript, all the code shares a single global namespace which is simply a single global object that holds all global variables and functions as properties. In the browser this is the window object. This tends to pollute the global scope if you have many objects. In the example below, num, obj, str, total, numr, and sum are all added to the global object:

var num = 5;var obj = {};var str = "Good morning";function sum(x, y){   total = x + y;   return total;}numr = sum(4,3);

Anything that is not properly declared, such as undeclared function variables, also ends up on the global object. In the example above, the identifiers num, obj, str, and sum are properly declared using the var keyword, but the function scoped variable total is missing a var and numr is a misspelling of num. JavaScript will add both total and numr to the global name space, which most likely is not what you want.

Suppose that your web app includes a third party JavaScript library that creates beautifully animated buttons. You deploy your app and the next thing you know is there are complaints from a client: they don't want 125,000 teddy bears. You start investigating and after many hours of intense pressure you discover that the animation library uses a global variable named number to iterate over the animations. But you also use number to hold the number of items ordered in a shopping cart. This is an example of a name collision. Name collisions can create bugs that are often very hard to trace.

Name collisions can be a significant problem in JavaScript. So, how do you avoid this? A quick and easy solution is offered by the Namespace Design Pattern. You create a single global object for your application and add all your variables and functions to this object. This allows you to namespace your code, make things tidier, and significantly reduce your chances of naming conflicts with third-party JavaScript libraries, widgets, etc.

Note that the namespace name itself is added to the global scope, so it is best that you create a unique name. Our example below uses MYAPP in uppercase, to emphasize the namespace.

var MYAPP = {};        // our unique namespace  MYAPP.num = 5;MYAPP.obj = {};MYAPP.str = "Good morning";MYAPP.sum = function(x, y){   var total = x + y;   return total;}MYAPP.num = MYAPP.sum(4,3);

The above namespace pattern is a great first step in avoiding potential name collisions. However, other, more sophisticated and robust patterns exist that can help you better manage namespaces in JavaScript. These and others techniques are presented in our JavaScript + jQuery Design Pattern Framework. To check it out click here. Previous Next

Using modules in JavaScript

Java (not JavaScript) has a keyword called package that provides a way to group semantically-related classes. This helps in the organization of a large number of classes. For instance, in a graphical drawing application, Ellipse, Rectangle, and Polygon are logically-related classes. So you'd like to bundle these in the same package called Draw. Unlike Java, JavaScript does not provide any special keyword for packages.

The solution in JavaScript is to build modules in which to organize your code. What is a module? A module is a self-contained piece of code that groups semantically-related variables and functions. Modules are not built-in constructs in JavaScript, but the JavaScript Module Pattern provides a way to create modules which have well-defined interfaces that are exposed to clients of the module.

An important advantage of modules is that the internal functionality can be modified whenever necessary without affecting the rest of your program. This promotes encapsulation and information hiding.

To define a module in JavaScript, you take advantage of anonymous closures by creating an anonymous immediate function. That is a mouthful, but in reality it is not too complicated. Once you've seen a few modules, you'll understand how they work.

Here is the outline of a module:

var MODULE = (function () {    var module = {};     var privateVariable = 4;	    function privateMethod() {        // ..    }	    module.moduleProperty = 1;    module.moduleMethod = function () {        // ...    };    return module;}());

This is a simple but good example that demonstrates the principles of building JavaScript modules. The anonymous immediate function is the function wrapped in parentheses, like so (function() { … })(). It is anonymous because it has no name, and it is immediate because when JavaScript encounters it, it gets executed immediately and its return value is assigned to MODULE.

The function's variables privateVariable and privateMethod are private to the function meaning that external code has no access to these variables. The moduleProperty and moduleMethod are part of the module object which is returned, so that part is public in the sense that outside code will have access to these properties.

Modules are fundamental to building modern JavaScript applications. Unfortunately, a full discussion of the Module pattern is beyond the scope of this tutorial. To learn more you will find a solid review of the Module pattern with plenty of examples in our JavaScript + jQuery Design Pattern Framework. To learn more click here.

Chaining JavaScript methods

JavaScript allows you to invoke multiple methods on an object in a single expression. This is called chaining and is accomplished by stringing the method calls together with dots between them, like so:

object.method1().method2().method3();

When building a chain the object is named only once and then multiple methods are called on it. For this to work, your methods must return the object that they operate on. Each method works on the object and when it is done it returns it to the next call. This gives rise to a chain of method calls in a single expression.

Here is a real-world example of a banking account in which the account number, the balance, and a line of credit are set:

account.number("012477630").setBalance(10971).applyCredit(200);

Chaining in JavaScript can improve performance as well as readability. The jQuery library uses chaining extensively; here is an example of how to chain jQuery selector methods:

$("#myDiv").removeClass("off").addClass("on").css("background": "red");  

This coding style cuts down significantly on jQuery's selector use, which is useful because usually this is where jQuery spends most of its time. Let's look at another jQuery example. Consider this piece of HTML code:

<ul class="horizontal_list">
    <li class="one">Home<li>
    <li class="two">About Us</li>
    <li class="three">Contact Us</li>
</ul>

We now use jQuery to make two background color changes; the Home menu item red and Contact Us light blue. Here is the jQuery chained expression that will accomplish this:

$("ul.horizontal_list")  .find(".one").css("background-color", "red").end()  .find(".three").css("background-color", "lightblue");

Run Clear

  • Home

  • About Us

  • Contact Us

First, the jQuery selector returns the entire ul element with class="horizontal_list". Next the find() method locates the item with class "one" within the ul element and the subsequent css() method sets its background color to red. The end() method tells the object to return to its initial state i.e. go back to the ul before the second find() is invoked. The second find() then searches for the item with class "three" inside the ul element and the next css() method turns its background to light blue. The outcome is that the Home and Contact Us items of the horizontal_list have colored backgrounds.

These examples clearly show the benefits of chaining. First, there is no need to store the return value of a previous method in a temporary variable. Second, it lets you perform multiple complex operations in a single concise, easy-to-read expression. And third, performance is better. This technique can be very beneficial in your own JavaScript projects.

A disadvantage of chaining is that it may be difficult to debug; in case of an error you have no idea which method in the chain failed.

To learn more about chaining, including details on how to implement methods that support chaining, we suggest you consult our JavaScript + jQuery Design Pattern Framework. To learn more click here.

Using closures to achieve privacy

Unlike most object-oriented programming languages, JavaScript does not support access modifiers such as, private, protected, and public to specify the accessibility of properties and methods in objects. In JavaScript, all object members are public. In the following example, both the author property and the getAuthor() method are public and therefore can be accessed from anywhere in the program.

var book = {    author: "James Joyce",    getAuthor: function () {        return this.author;    }};alert(book.author);       // => James Joyce  (public property)alert(book.getAuthor());  // => James Joyce  (public method) 

Run

You may think that this is because we are using an object literal to create the book instance. However, creating an instance using a Book constructor function will also result in public properties and public methods as the following example demonstrates.

function Book () {    this.author = "James Joyce";    this.getAuthor = function() {        return this.author;    }}var book = new Book();alert(book.author);       // => James Joyce   (public property)alert(book.getAuthor());  // => James Joyce   (public method)

Run

With object members being so exposed, is there perhaps a way to protect these in JavaScript? The answer is yes, by using function closures.

Going back to the Book example, the objective is to keep the author data private without exposing it to the outside world. The way to do this is to define an author variable within the function. The functions closure ensures that it is only accessible within the function's scope. So, instead of assigning author to this, you create a local variable called author.

function Book () {    var author = "James Joyce";     // private    this.getAuthor = function() {   // privileged         return author;    }}var book = new Book();alert(book.author);             // => undefined (i.e. private)alert(book.getAuthor());        // => "James Joyce" 

Run

Closure is an important and powerful concept in JavaScript. Here it allows us to keep author private. The getAuthor() method is called a privileged method because it has access to the private author variable and is itself accessible to the outside world as a public method on the book instance.

Achieving privacy when you create an object using object literal

You can achieve the same privacy level by using an anonymous immediate function. An immediate function is wrapped in brackets and executes immediately. The example below demonstrates enclosure with an immediate function. The function's closure maintains the value of author. Furthermore, notice that the book object is created with object literal notation.

var book;                         // public object declaration(function () {                    // anonymous immediate function     var author = "James Joyce";   // private member    book = {       getAuthor: function () {   // privileged method            return author;                }    };}());alert(book.author);        // => undefined    (author is private)alert(book.getAuthor());   // => James Joyce  (privileged method)

Run

Similar to the earlier example, the getAuthor() method is also privileged with privileged access to the local private variable author.

Anonymous immediate functions are frequently used in modern JavaScript applications and are also used extensively in our JavaScript + jQuery Design Pattern Framework. Click here to learn more.

Sharing private members among common instances

Suppose you have a variable that is common to all object instances, but you want to keep that private. In other languages you would create a static variable. Unfortunately, JavaScript does not support static or class variables.

You can obtain similar results in JavaScript by adding the common (i.e. shared) members to the prototype property of the constructor. In the example below, the label variable is shared among all the object instances created using the Book() constructor function. The label property is only accessible through the getLabel() prototype method.

function Book(author) {   var author = author;          // private instance variable    this.getAuthor = function () {      return author;             // privileged instance method   };}Book.prototype = (function () {   var label = "Author: ";       // private prototype variable   return {       getLabel: function () {   // privileged prototype method           return label;       }     };}());var book1 = new Book('James Joyce');alert(book1.getLabel() + book1.getAuthor()); // => Author: James Joycevar book2 = new Book('Virginia Woolf');alert(book2.getLabel() + book2.getAuthor()); // => Author: Virginia Woolf

Run

Prototype functions are shared by all object instances. They are often used because it removes the need to create a function for each instance which saves memory and performs better. It is important to note that prototype functions have only access to prototype variables and not to private variables, such as the author variable in the example above.

Don't pass references to your private objects

In the example above, the private variable author is a string. However, if author was an object, then the getAuthor method would return it by reference to the outside world (remember that primitive variables are passed by value and objects are passed by reference). Directly returning a private object reference from a privileged method essentially cancels privacy. Anyone can now make changes to the author object. Let's look at an example.

function Book(author, price) {    var details = {        "author": author,        "price": price    };    this.getDetails = function () {     // anti pattern         return details;                 // returns reference to details    };}var book = new Book("James Joyce", "29.50");var bookDetails = book.getDetails();bookDetails.author = "Jane Austen";     // modifies private data bookDetails.price = "99.95";alert(book.getDetails().author);        // => Jane Austenalert(book.getDetails().price);         // => 99.95

Run

Although the details variable is private, the method getDetails() passes it by reference to the code outside the object. Therefore it is exposed to modification by clients of the Book which is shown in the last four statements in which both the author and price are updated.

One possible solution would be to clone the details object in the getAuthor() method and then return the clone. Alternatively you could return a new object that contains only those properties of the details object that the outside code is interested in and return it.

Last updated

Navigation

Lionel

@Copyright 2023