Constructors

IIFEs let you take advantage of function scope to create namespaces to organize large pieces of your code. There is another use of functions that makes them act like factories for objects that all have similar properties and methods. In other languages, you might use a class for this kind of organization. Strictly speaking, JavaScript does not have classes, but it does give you a way to create custom types.

You have already started to create the DataStore type. Now you will customize it in two steps. In the first step, you will give it a property that will be used internally for storing data. In the second step, you will give it a set of methods for interacting with that data. You do not need to give other objects direct access to that data, so this type will provide an external interface through a set of methods.

Object factory functions are called constructors in JavaScript.

Add the following code to the body of the DataStore function in datastore.js.

(function (window) {
  'use strict';
  var App = window.App || {};

  function DataStore() {
    console.log('running the DataStore function');
    this.data = {};
  }

  App.DataStore = DataStore;
  window.App = App;
})(window);

The job of a constructor is to create and customize a new object. Inside the body of the constructor, you can refer to that new object with the keyword this. You used the dot operator to create a property named data on your new object and assigned an empty object to data.

You may have noticed that you capitalized the first letter of DataStore. This is a convention in JavaScript when naming constructors. It is not necessary, but it is a good practice as a way to tell other developers that the function should be used as a constructor.

To differentiate a constructor from a regular function, you use the keyword new when you call it. This tells JavaScript to create a new object, set up the reference from this to that new object, and to implicitly return that object. That means it will return the object without an explicit return statement in the constructor.

Save and return to the console. To learn how to use a constructor, you are going to create two DataStore objects (or instances) and add values to them. Begin by creating the instances.

var dsOne = new App.DataStore();
var dsTwo = new App.DataStore();

You created these DataStore instances by calling the DataStore constructor. At this point, each has an empty data property. Add some values to them:

dsOne.data['email'] = '[email protected]';
dsOne.data['order'] = 'black coffee';
dsTwo.data['email'] = '[email protected]';
dsTwo.data['order'] = 'chai tea';

Then inspect the values:

dsOne.data;
dsTwo.data;

The results tell you that each instance holds different information (Figure 8.8).

Figure 8.8  Saving values to instances of the DataStore constructor

Saving values to instances of the DataStore constructor

A constructor’s prototype

Using a DataStore instance, you can manually store and retrieve data. But, in its current form, DataStore is just a roundabout way of creating an object literal, and any module that will use a DataStore instance has to be coded to use the data property directly.

This is not good software design. It would be better if DataStore provided a public interface for adding, removing, and retrieving data – all while keeping the details of how it works a secret.

The second part of creating your custom DataStore type is to provide these methods for interacting with the data. The goal is for these methods to serve as the interface that other modules use when they interact with a DataStore instance. To accomplish this, you will make use of a very cool feature of JavaScript functions, the prototype property.

Functions in JavaScript are also objects. This means that they can have properties. In JavaScript, all instances created by a constructor have access to a shared storehouse of properties and methods: the prototype property of the constructor.

To create these instances, you used the new keyword when you called the constructor. The new keyword not only creates your instance and returns it but also creates a special link from the instance to the constructor’s prototype property. This link exists for any instance created when the constructor is created with the new keyword.

When you add a property to the prototype and assign it a function, every instance you create with the constructor will have access to that function. You can use the keyword this inside of the function body, and it will refer to the instance.

To see this in action, create the add function in datastore.js as a property of the prototype. You can also delete the call to console.log.

(function (window) {
  'use strict';
  var App = window.App || {};

  function DataStore() {
    console.log('running the DataStore function');
    this.data = {};
  }

  DataStore.prototype.add = function (key, val) {
    this.data[key] = val;
  };

  App.DataStore = DataStore;
  window.App = App;
})(window);

You gave DataStore.prototype the property add and you assigned a function to it. That function takes two arguments, key and val. Inside the function body, you used those arguments to make changes to the instance’s data property.

In terms of how DataStore works with coffee orders, it will store the order information (the val), using the customer’s email address (the key).

You are not setting up a true database, but DataStore works well enough for CoffeeRun. It is able to save some information, val, under the unique identifier specified by key. Because you are using a JavaScript object for storage, each key is guaranteed to be a unique entry in the database. (In a JavaScript object, a property name is always unique, like function names within a namespace. If you tried to store different values using the same key, you would just overwrite any previous values for that key.)

This aspect of JavaScript objects fulfills the one major requirement of any database: keeping the individual pieces of data separate.

Save your code and switch back to the browser. Create an instance of DataStore in the console and use its add method to store some information.

var ds = new App.DataStore();
ds.add('email', '[email protected]');
ds.add('order', 'triple espresso');
ds.data;

Inspect the data property to confirm that it works (Figure 8.9).

Figure 8.9  Calling a prototype method

Calling a prototype method

Adding methods to the constructor

The next thing to do is to create methods for accessing the data. In datastore.js, add a method to look up a value based on a given key and one to look up all keys and values.

...
  DataStore.prototype.add = function (key, val) {
    this.data[key] = val;
  };

  DataStore.prototype.get = function (key) {
    return this.data[key];
  };

  DataStore.prototype.getAll = function () {
    return this.data;
  };

  App.DataStore = DataStore;
  window.App = App;
})(window);

You created a get method that accepts a key, looks up the value for it in the instance’s data property, and returns it. You also created a getAll method. It is almost the same, but instead of looking up the value for a single key, it returns a reference to the data property.

You can now add and retrieve information from a DataStore instance. To complete the cycle, you need to add a method for removing information. Add that to datastore.js now.

...
  DataStore.prototype.getAll = function () {
    return this.data;
  };

  DataStore.prototype.remove = function (key) {
    delete this.data[key];
  };

  App.DataStore = DataStore;
  window.App = App;
})(window);

The delete operator removes a key/value pair from an object when your new remove method is called.

With that, you have completed the DataStore module, which provides the most important part of the CoffeeRun application. It can store data, provide stored data in response to queries, and delete unnecessary data on command.

To see all of your methods in action, save your code and go to the console after browser-sync has reloaded your browser. Enter the following code, which exercises all of the methods of DataStore:

var ds = new App.DataStore();
ds.add('[email protected]', 'tea');
ds.add('[email protected]', 'eshpressho');
ds.getAll();
ds.remove('[email protected]');
ds.getAll();
ds.get('[email protected]');
ds.get('[email protected]');

As shown in Figure 8.10, DataStore’s instance methods should now work as expected. These methods are exactly the way that other modules will interact with your application’s database.

Figure 8.10  Working with DataStore using only its prototype methods

Working with DataStore using only its prototype methods

Your next module will use the same structure: an IIFE with a parameter for the namespace to modify. But it will provide completely different functionality from DataStore.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.244.250