What you will learn
In this chapter, we are going to take our JavaScript knowledge to the next level. We are going to discover how applications manage errors with exceptions and how to design data storage with classes. We’ll discover how class inheritance can save us time when creating applications and how we can use object-oriented techniques to make data components that can look after themselves. We’ll be doing this by creating a fully featured data storage application you could use to run a business.
Investigate using super
to create instances
Investigate the getDescription
method
In Chapter 9, when we were looking at JSON, we discovered that JSON uses exceptions to signal when things go wrong. An exception is an object that describes something bad that has just happened. A piece of JavaScript can raise
or throw
an exception to interrupt a running program. Now is the time to find out what this means and discover the part that exceptions play in creating reliable applications.
If you remember, JSON (JavaScript Object Notation) is a standard for encoding the contents of JavaScript variables into text so that they can be stored or transferred to another machine. We used JSON to convert the Tiny Contacts store into a string so that it could be stored using local storage in the browser. The JSON object provides methods called stringify
and parse
that can move JavaScript objects to and from text strings.
var test = {}; test.name = "Rob Miles"; test.age = 21; var JSONstring = JSON.stringify(test);
The statements above create a JavaScript object called test
, which contains name
and age
properties. The contents of this object are then converted into a string called JSONstring
by the stringify
method.
{"name":"Rob", "age":21}
This is the string that would be stored in JSONstring
. This string can be converted back into a JavaScript object by using the JSON parse
method:
var test = JSON.parse(jsonString);
MAKE SOMETHING HAPPEN
Breaking JSON
The stringify
and parse
methods will fail if they are given invalid inputs. They fail by raising or throwing an exception. Let’s investigate what this means and how programs can be made to handle this failure. Use your browser to open the example application in the Ch10 Advanced JavaScriptCh10-01 JSON Validator examples folder.
This application tests strings of text to find out if they contain valid JSON. It works by handing exceptions thrown by the JSON parse
method. When the Parse button is pressed, the application reads the string from the input and displays whether the string contains valid JSON. Look back through Chapter 9 to find some valid JSON to use to test it. Try it with a few strings to prove that it works.
Now press F12 to open the Developer View and select the Console tab so that we can investigate the application. The JSON parse
method fails by throwing an exception. Let’s see what that means. Type in the following statement, which attempts to parse a string with the dangerous sounding contents kaboom
. Press Enter to see what happens.
> JSON.parse("kaboom");
The JSON parse
method is not able to make sense of kaboom
, and it indicates this by throwing an exception. We have not included code to catch the exception, so the JavaScript console displays an error message in red:
> JSON.parse("kaboom"); Uncaught SyntaxError: Unexpected token k in JSON at position 0 at JSON.parse (<anonymous>) at <anonymous>:1:6
We are not used to JavaScript programs complaining in the event of an error in the program code. If our program combines values incorrectly, JavaScript will use the values Not a Number (NaN
), undefined
, or overflow
to indicate that something has gone wrong. Our program must test for these values to decide whether an action has worked properly.
The error created by parse
is called an exception. It denotes the fact that the requested action can’t be performed. If the exception is not caught, the sequence of execution will end. Statements that might throw an exception can be placed inside a try
block of code. The try
block is followed by a catch
block, which contains statements to be performed if an exception is thrown. This is called a try-catch
construction. Let’s type one in. Enter the code below, remembering to press Enter at the end of every line.
> try {
JSON.parse("kaboom");
} catch {
console.log("bad json");
}
When you press Enter after the closing curly bracket of the catch
block, the JavaScript code runs. The parse
method will fail and throw an exception, but this time the exception is thrown inside a try
block, and the associated catch
clause will run and log a message on the console:
> try {
JSON.parse("kaboom");
} catch {
console.log("bad json");
}
bad json
Enter the same construction replacing the word kaboom
with some valid JSON, such as {}
. Note that this time, the bad json
message is not displayed because parse
doesn’t throw an exception if the JSON string is valid.
The JSON validator above uses the try-catch
construction to display an appropriate message.
The doValidate
function is called when the Parse button is pressed in the JSON validator web page. If the user types in valid JSON, the JSON.parse
method doesn’t throw an exception, and none of the statements in the catch
block are performed. Instead, execution goes straight to the end of the method. However, if the JSON.parse
method can’t parse the string that was typed in, the exception is generated and execution transfers immediately to the block of code under the catch
keyword.
The try
block can contain many statements. However, this might make it hard for you to work out which statement caused the exception.
CODE ANALYSIS
Exception handling
You might have some questions about how exceptions are handled.
var result = JSON.parse(inputText); outputElement.innerText = "Valid JSON";
throw "something bad happened";
throw "something bad happened"; console.log("This message is never printed");
> var infiniteLoop = {}
Creating variables always returns an undefined result, so you should see the undefined
message.
> var infiniteLoop = {} <- undefined
Now we are going to add a property to the object that contains a reference that refers to the object itself. Type in the following statement and press Enter.
> infiniteLoop.loopRef = infiniteLoop
When you press Enter, the JavaScript console adds a new property to the infiniteLoop
variable that contains a reference to the infiniteLoop
variable. In other words, this variable now contains a reference to itself.
> infiniteLoop.loopRef = infiniteLoop
<- {loopRef: {…}}
Now, let’s try and stringify
the value in infiniteLoop
. Type the following and press Enter:
> JSON.stringify(infiniteLoop)
When you press Enter the stringify
method tries to save the contents of infiniteLoop
. It does this by working through each of the properties inside the value and saving each one in turn. It finds the loopRef
property, so it follows that reference to save that value. The reference leads to the infinfiteLoop
variable, so with stringify
, this save process would go on forever, rather like a reflection between two parallel mirrors. Fortunately, the people who created the stringify
method are aware of the problem and have added a test for what is called a “circular” reference. If you try to stringify
an object that contains a reference to itself, you will get an error.
> JSON.stringify(infiniteLoop) Uncaught TypeError: Converting circular structure to JSON --> starting at object with constructor 'Object' --- property 'loopRef' closes the circle at JSON.stringify (<anonymous>) at <anonymous>:1:6
Now that we know how to throw and catch exceptions, we can consider how we can use them in our applications. However, before we do that, I want to talk about the two types of fault that can occur in a program:
Things that shouldn’t happen
Things that really shouldn’t happen.
Things that shouldn’t happen include users typing in numbers that are out of range (perhaps an age value of -99) and network connections failing. These are bad things that we expect. Things that really shouldn’t happen include faults in functions and methods that are used by our applications.
The starting point for the discussion of exceptions was the way that the JSON.parse
method throws an exception if it is used to parse a string that does not contain valid JSON (for example, “kaboom”). This should never happen in the Tiny Contacts program. The only way that this could happen is if the browser storage is corrupted in some way. Is this really a problem? I would say yes, and I’ve added code to my Tiny Contacts application to deal with this.
The code above is a modified version of the loadDataStore
function from the Tiny Contacts application in the example programs in the folder Ch10 Advanced JavaScriptCh10-03 Tiny Contacts Secure. The call of the JSON.parse
method is now enclosed in a try
block, and the catch
block creates an empty contact store if the parse
method fails with an exception. The first version of loadDataStore
returned the Boolean value true
if it worked and false
if it failed. There are three possible ways that this version of loadDataStore
can complete:
The data is not present in local storage because this is the first time the application has been used.
The data loaded from local storage is not valid JSON. (This will cause an exception in parse
.)
The data was found and loaded successfully.
The function returns one of three status values to indicate which of these possible outcomes happened when loadDataStore
was called. Programmers can then test this value to determine what happened when the contacts store was loaded.
These values have been declared using the keyword const
. This means that their value cannot be changed by the program when it runs. This is sensible because they are being used to indicate status values. The return values from loadDataStore
are used by the function doStartTinyData
. If the store is empty or invalid, an alert is displayed for the user.
It is very important that once we have created some error-handling code, we also create a way of testing it. In this case, I created a new application that breaks the storage. You can see it in Figure 10-1 and find the application itself in the folder Ch10 Advanced JavaScriptCh10-04 Store Breaker in the examples. You can use this application to “break” the data storage for the Tiny Contacts application.
<!DOCTYPE html> <html lang="en"> <head> <title>Store Breaker</title> <link rel="stylesheet" href="styles.css"> <script src="breakstore.js"></script> </head> <p class="menuHeading"> Store breaker</p> <p> <button class="menuButton" onclick="doBreakStore()">Corrupt the data store</button> </p> <p> <button class="menuButton" onclick="doEmptyStore()">Remove the data store</button> </p> </body> </html>
This is the HTML for the store breaker application. It contains two buttons. One button is pressed to corrupt the data store. It stores the string kaboom
in the data store, which will cause JSON.parse
to fail. The second button is pressed to remove the data store completely. These allow us to test the two possible errors.
These functions use a feature of JavaScript that we haven’t seen before. The confirm
function allows a user to confirm an action. It pops up a message box containing the prompt string and offers the user a chance to either confirm the action or cancel. If the user confirms the action by clicking the OK button, as shown in Figure 10-2, the confirm
function returns the value true
.
From what we’ve learned up to now, we can regard a Java object as a container. We’ve seen that a program can add properties to an object so that it can assemble related items. We explored this by creating an object-based application that stores contact details. Object properties were added by the program one at a time, building up a complete set of contact information by adding name, address, and phone number properties to an “empty” object. This works well for small applications, but sometimes you want to map out your class design, rather than building it up as the program runs. And, as we shall see, using classes to design objects also brings benefits by reducing the amount of code that we write.
Your lawyer client is very happy with her Time Tracker application. She’s been showing it to her friends, and they’ve been very impressed—particularly a friend who runs a fashion shop and has been looking for an application to help her manage her stock. She sells a range of clothing items and needs help tracking inventory. She’s keen to get your help, and she’s offering discounted prices or even free clothing in exchange. Free fashion sounds like an interesting idea, so you sit down with your new client and talk about what she needs.
She tells you that stock arrives from suppliers, and she enters the details in her stock book. For each different item that she sells, she stores a page of data in the book. She updates the stock level in the book when stock arrives from her suppliers and when she sells something. She shows you two of the pages from her book.
Figure 10-3 shows us what Imogen does when she works with the stock data. We can use this as the basis of our program specification. As usual, we draw some designs showing how the program will be used. There will be more than one design because this program will be spread across several “pages.” For each page, we will need to also write a story that describes how the page will be used. This will also allow us to work through the feature provided by that page to make sure that we know exactly what the program should do. “User stories” are an important part of software design. You can find out more about them at https://www.mountaingoatsoftware.com/agile/user-stories.
Figure 10-4 shows the main menu of the program. Imogen can click the buttons to select pages to add dresses, pants, skirts, and tops to her stock. She can select an update page to edit any existing stock items to change their description or stock levels. She can also obtain a list of all her stock items by pressing the List button.
Figure 10-5 shows what Imogen would see when she presses the Dress button on the main menu to add a dress to stock. When she clicks Save on this page, the program will assign a stock number to the item and save it.
When a new item is added to the store, the program displays an alert, as shown in Figure 10-6. This gives the stock number, which was assigned to the new item.
The Update stock item button on the main menu starts a search for a given stock number that is to be updated. Figure 10-7 shows the search page. When the Find button is clicked, the program will search for a stock item with the given id
value and display the item for editing.
The final button on the main menu shown in Figure 10-4 is the List button. Imogen will press this to generate a list of stock items that she can look through. Each item has an Update button that she can click (see Figure 10-8) to open the update page for that item.
Imogen thinks this will be a good start for the application; you agree a price in stylish clothing and start working on the program. The first thing that you need to do is decide how the different stock items are going to be stored.
MAKE SOMETHING HAPPEN
Manage Imogen’s Fashions
The application in the sample folder Ch10 Advanced JavaScriptCh10-05 Fashion Shop implements a working fashion shop store. It creates a set of test data that you can view. You can also create your own fashion items, store them, and then search for them by their stock reference. You should spend some time adding and editing stock items to get a feel for how it is used. Then we can start working out how each part works.
We could store information for a particular stock item in a JavaScript object by creating an empty object and then adding properties to it:
The statements above create an object called myDress
that contains all the data for the dress in Figure 10-3. However, there is a much easier way of creating objects that contain properties. We can create a class
that tells JavaScript how to make a Dress
object and what the object contains:
class Dress{ constructor(stockRef, stockLevel, price, description, color, pattern, size){ this.stockRef = stockRef; this.stockLevel = stockLevel; this.price = price; this.description = description; this.color = color; this.pattern = pattern; this.size = size; } }
The JavaScript above doesn’t store any data. Instead, it tells JavaScript the properties that are stored inside a Dress
object and how to construct one. The constructor method is called to create an instance of the Dress
class. Now we can create a new Dress
much more easily:
myDress=new Dress(221,8,60,"Strapless evening dress","red","swirly",10);
The new
keyword tells JavaScript to find the specified class and run the constructor method in that class to create a new instance of the class. When the constructor method runs, it copies the parameter values into properties in the newly created object. The keyword this
, which you can see in the constructor method above, means “a reference to the object that this method is running inside.”
The confusing-looking statement above takes the price value that was supplied as an argument to the constructor and assigns it into a newly created price
property on the object that is being created.
yourDress=new Dress(221,2,50,"Elegant party dress","blue","plain",12); herDress=new Dress(222,5,65,"Floaty summer dress","green","floral",10);
If the word this
is confusing, consider the two statements above. They create two Dress
instances. Each time the constructor in the Dress
class runs, it must set up a different object. In the first call of the constructor to set up the yourDress
the keyword, this
represents a reference to the yourDress
object. In the second call of the constructor to set up herDress
, the keyword this
represents “a reference to the herDress
object.”
CODE ANALYSIS
Objects and constructors
shortDress = new Dress(221,0,50);
It would make sense to create a class to hold each kind of data we wish to store. Programmers call this object-oriented programming. The idea is that elements in a solution are represented by software “objects.” The first step in creating an application is to identify these objects.
In the English language, words that identify things are called nouns. When trying to work out what classes a system should contain, it’s a good idea to look through the description of a system and find all the nouns. As an example, consider the following description of a fast-food delivery application.
“The customer will select a dish from the menu and add it to his order.”
I’ve identified four nouns in this description, each of which will map to a specific class in the application. If I were working for the fast-food delivery company, I would next ask them what data they stored about customers, dishes, menus, and orders.
When we talk to our fashion shop customer, she’ll talk about the dresses, pants, hats, tops, and other items that she wants the application to manage. Each of these could be objects in the application and can be represented by a class. Each class will contain the properties that describe that item of clothing. Let’s start by considering just the information for dresses and pants and create some classes for these objects. We already have a class for Dress
, so let’s make one for Pants
.
class Pants{ constructor(stockRef, stockLevel, price, description, color, pattern, length, waist){ this.stockRef = stockRef; this.stockLevel = stockLevel; this.price=price; this.description=description; this.color = color; this.pattern = pattern; this.length = length; this.waist = waist; } }
The code above defines a Pants
class. It contains a constructor
method to set up the contents of that class. Our program can now create instances of these classes:
myDress=new Dress(221,8,60,"Strapless evening dress","red","swirly",10); myPants=new Pants(222,1,45,"Good for the workplace","black","plain",30,30);
When I wrote this sample code, I found myself using a lot of block-copy
commands in the editor when I created the constructor. This is not necessarily a good thing.
JavaScript classes support a mechanism called inheritance. This is another aspect of object-oriented design. Inheritance lets us base one class on an existing superclass. This is called extending the superclass. We can greatly simplify the design of our classes for the Fashion Shop program by creating a superclass, which we can call StockItem
.
The StockItem
class will store all the attributes common to all the data items in the shop. These are the stock reference, price, color, and stock level. The Dress
and Pants
classes will extend the StockItem
class and add the properties particular to dresses and pants. Figure 10-9 shows the arrangement of the classes we’re creating. In software design terms, this is called a class diagram.
The class diagram shows the relationship between classes in a system. Figure 10-9 shows that both Pants
and Dress
are subclasses of the StockItem
class (meaning they are based on that class). We could also say that the StockItem
class is the superclass of Dress
and Pants
.
In real life, inheritance means stuff that you get from people who are older than you. In JavaScript terms, inheritance means the attributes a subclass gets from its superclass. Some programmers call the superclass the parent class and the subclass the child class.
The key to understanding inheritance is to focus on the problem it is solving. We’re working with a collection of related data items. The related items have some properties in common. We want to implement the shared properties in a superclass and then use this superclass as the basis of subclasses that will hold data specific to their item type. That way, we only need to implement the common properties once, and any faults in the implementation of those properties need only be fixed once.
Working in this way has another advantage. If the fashion shop owner decides that she would find it useful to be able to store the manufacturer of the items she’s selling, we can add a manufacturer attribute to the StockItem
class, and all the subclasses will inherit that attribute, too. This will be easier than adding the attribute to each class.
Another way to think of this is to consider what we are doing as abstraction. We first encountered abstraction in Chapter 9 in the section “Use a data schema,” where we created a design for all data stores rather than building individual ones. We have seen abstraction means “stepping back” from the problem and taking a more general view. In our conversations with the fashion shop owner, we would like to talk in general terms about the things she would like to do with the stock in her shop. She will want to add stock items, sell stock items, find out what stock items she has, and so on. We can talk to her about her stock in abstract terms and then later go back to fill in the specific details about each type of stock and give them appropriate behaviors.
Programmers use abstraction a lot. They talk about things like stock items, customers, and orders without considering specific details. Later, they can go back and “fill in the details” and decide what particular kinds of stock items, customers, and orders with which the application will work. We’ll create different kinds of stock items in our Fashion Shop program. The StockItem
class will contain the fundamental properties for all the stock, and the subclasses will represent more specific items.
The diagram in Figure 10-9 is called a class hierarchy. It shows the superclass at the top and subclasses below. When you travel down a class hierarchy, you should find that you move from the abstract toward the more concrete. The least abstract classes are Pants
and Dress
because these represent actual physical objects in our application.
CODE ANALYSIS
Understanding inheritance
Here are some questions about object-oriented design and inheritance. Try to come up with your own answers before reading the answers I’ve provided.
Extend the StockItem
class to make a Customer
subclass that contains the customer details because customers buy stock.
Add customer details to each StockItem
.
Create a new Customer
class that contains a list of the StockItems
that the customer has bought.
Now that we’ve decided using inheritance is a good idea, we need to consider how to make it work with our classes.
class StockItem{ constructor(stockRef, stockLevel, price, description, color){ this.stockRef = stockRef; this.price=price; this.description=description; this.stockLevel = stockLevel; this.color = color; } }
This is the StockItem
class file. It contains a constructor method to set up a StockItem
instance. The StockItem
class will be the superclass of all the objects that the fashion shop will be selling. We can create a Dress
class that is a subclass of the StockItem
class to hold information about dresses that the fashion shop will be selling.
class BrokenDress extends StockItem{ constructor(stockRef, stockLevel, price, description, color, pattern, size){ this.pattern = pattern; this.size = size; } }
The BrokenDress
class extends the StockItem
class. It only contains the properties that are specific dresses. However, we have a problem if we try to use the BrokenDress
class:
myDress=new BrokenDress(221,8,60,"Strapless evening dress","red","swirly",10);
The statement above tries to create an instance of the BrokenDress
class. This statement will fail with an error:
Uncaught ReferenceError: Must call super constructor in derived class before accessing ‘this’ or returning from derived constructor at new BrokenDress
JavaScript is telling us that to create a BrokenDress
, our constructor must first create a StockItem
. The constructor in the BrokenDress
class that we have created doesn’t do this, so this class is broken (hence the name). To fix it, we need to make a Dress
class that contains a constructor that first constructs the super object. JavaScript provides the super
keyword, which can be used in a constructor to call the constructor in the superclass. The constructor method for the Dress
class calls the constructor method in the StockItem
class by means of the super
keyword.
MAKE SOMETHING HAPPEN
Investigate using super
to create instances
The example application in the folder Ch10 Advanced JavaScriptCh10-06 Fashion Shop Classes contains the StockItem
, BrokenDress
, and Dress
classes that you can experiment with. You can use the Developer View to debug the process of creating a Dress
by placing a breakpoint at the first statement of the Dress
constructor and then stepping through the JavaScript as the super
keyword is used to call the constructor in the StockItem
.
Things that are part of a class are called the members of the class. We know how to create properties which are members of a class. Now we are going to find out how to add method members. Adding a method member to a class allows it to do things for our program. At the moment, the classes we have created don’t contain any methods. A useful method might be one that allows a Dress
to provide us with a string describing its contents. We can use this to produce the text to be displayed for the Stock List menu item.
The Dress
class above contains a method called getDescription
that can be called to get a description of the contents of the object. Note that the method uses the this
reference to access the properties of the object that is being described. A program can use this method to get a string that describes a particular dress.
myDress=new Dress(221,8,60,"Strapless evening dress","red","swirly",10); console.log(myDress.getDescription());
The first statement above creates a dress object called myDress
. The second statement uses the getDescription
method on the myDress
object to display a description of the dress. It would display the following:
Ref:221 Price:60 Stock:8 Description:Strapless evening dress Color:red Pattern:swirly Size:10
The Fashion Shop application will use the getDescription
method to build an HTML element to be displayed in a stock list.
MAKE SOMETHING HAPPEN
Investigate the getDescription
method
The example application in the Ch10 Advanced JavaScript Ch10-07 Fashion Shop Methods folder contains the StockItem
, Dress
, and Pants
classes that you can experiment with. You can use the Developer View to create Dress
and Pants
instances and call the getDescription
method to view their contents.
Objects and polymorphism
The next thing I want to talk about has the most impressive name in the entire book. The word polymorphism comes from the Greek language and means “the condition of occurring in multiple forms.” In software engineering, it means regarding an object in terms of what it can do rather than what it is.
A great thing about the getDescription
method is that other parts of the Fashion Shop application don’t need to know how the Dress
and Pants
classes store their data or even what data is stored inside them. A part of the program that needs to produce a description of a Dress
doesn’t have to pull out the various properties from a Dress
instance; it just has to call the getDescription
method to get a string that describes that particular dress. What’s more, this part of the program doesn’t need to care whether it is dealing with dresses or pants, it can just view these items as “things I can use getDescription
to get a description of.”
Polymorphism means thinking about objects in terms of what they can do, rather than what they are, and allowing each object to perform a particular action in a way specific to that object. A given object can be viewed in many ways, depending on what you want to do with it. Different parts of the Fashion Shop will view objects in terms of abilities such as “get a description string,” “set a discount,” “save,” and “load.” Each of these abilities can be provided by methods inside the object with a characteristic name, which can then be used by the rest of the system to perform that action. Part of an object-based design process involves identifying the behaviors required of objects and specifying them as methods.
A fundamental principle of object-oriented design is that a given object contains all the behaviors for that particular object. In this respect the getDescription
method in the Dress
class is not good. The first four items that are used to build the description string are not held in the Dress
class. Instead, they are held in the StockItem
superclass.
If we added a new property to the StockItem
class, we would have to also change the getDescription
method in the Dress
class to display the new property. If StockItem
had many subclasses, we would have to change every one. What we would like to do is make the StockItem
class responsible for delivering the description of what it contains, and then use that description in the Dress
class. It turns out that we can do this by creating a getDescription
method in the StockItem
class and then overriding the getDescription
method in subclasses of StockItem
.
Both StockItem
and Dress
contain a getDescription
method. We say that the getDescription
method in the Dress
class overrides the getDescription
method in the StockItem
class. A description of a Dress
must include a description of the contents of the super object of Dress
, so the JavaScript super
keyword is used to invoke the getDescription
method in the StockItem
object. Within a method in a class the word super
is a reference to the super
object (the one above the object in the class hierarchy).
CODE ANALYSIS
Understanding super
The example application in the folder Ch10 Advanced JavaScript Ch10-08 Dress Shop Override Methods contains the StockItem
, Dress
, and Pants
classes that work in exactly the same way as the previous example. But these versions use overridden versions of the getDescription
method. You can use the debugger to explore how the overridden method is called. However, you might also have some questions.
You will have noticed that the demonstration Fashion Shop app contains a lot of test data. This is generated by the data objects themselves. The data and methods to create test dresses are not part of any Dress
instance. Instead, they are part of the Dress
class itself. JavaScript lets us do this by creating properties and methods, which are static. The word “static” in this context means “always there,” rather than unchanging. We don’t need to use new
to create an instance of the Dress
class to get hold of the static members of the class. These members exist as soon as the JavaScript class is loaded by the browser.
The method getTestItems
above works through arrays of colors, patterns, and sizes to create a large number of dress stock items, which it adds to an array supplied as a parameter. The getTestItems
method is static
, so it can be called without the program needing to make an instance of the Dress
class. The data arrays that are used by the getTestItems
method are also defined as static
. The getTestItems
method also uses the static
method getRandomInt
, which is declared in the StockItem
class. The getRandomInt
method is used to obtain random prices and stock levels for the dresses that are created. It is based on the function that we created to make a random dice. The getLargestStockRef
method is used to search the fashion store to find the highest stock number in it. This ensures that the function doesn’t create any stock items with the same stock number as existing ones.
The static members of a class are marked with the static
keyword. Static class members are accessed via the class identifier so the above method can be used as follows to create an array full of Dress
values:
demo = [] Dress.getTestItems(demo);
CODE ANALYSIS
Understanding static
The example application in the folder Ch10 Advanced JavaScript Ch10-09 Fashion Shop Static Members contains Dress
and Pants
classes that contain static getTestItems
method that create large amounts of data that can be used to test our system. However, you might also have some questions.
We have one last problem to solve before we can build the finished solution. We need a way of saving the dress shop data. The Tiny Contacts application that we created in Chapter 9 used JSON to encode the contact objects into text that was then stored as strings in browser local storage. We can do something similar, but the use of a class hierarchy makes it a little bit trickier. To understand why, consider what happens when we convert a dress value to a string using the JSON.stringify
method.
{"stockRef":1,"stockLevel":"11","price":"85", "description":"red plain dress","color":"red","pattern":"plain","size":"8"}
The text above is a JSON string that describes a dress. It contains all the properties in a Dress
instance, including those in the StockItem
object superclass. When we load this object back into our program we want to be able to use it as a Dress
object. Unfortunately, there is nothing in the JSON string that tells a program reading it that this is a stored Dress
value.
The way to fix this is to add an extra property to our data that gives the type of the data. We can do this in the constructor for the class:
This is the modified Dress
constructor. We can modify the constructor for the other types of clothing so that they create an appropriate type
property. For example, the Pants
constructor must set a type
value of "pants"
and so on.
type
property{"stockRef":1,"stockLevel":"11","price":"85","type":"dress", "description":"red plain dress","color":"red","pattern":"plain","size":"8"}
The JSON above describes a dress that contains a type
property. I’ve highlighted the type information in yellow. What we need now is a way of creating a Dress
instance from this JSON source string. I created a member function in the StockItem
class to do this. The function is called JSONparse
. It takes in a string of JSON and returns an object of the type specified by the type
value in JSON. It uses a switch
construction to decide what type of object to create.
The JSONparse
method also uses a method that we have not seen before. It is called Object.assign
, and you can see it used in the last but one statement in the method. The Object.assign
function copies all the data properties from one object to another. The program uses this to take all the data properties from the JSON object that we read and copy them into the empty object that we have just created. The first argument to the assign
function is the destination object for the copy. This is the newly created object of the required type. The second argument to the assign
function is the rawObject
that was loaded from JSON. This contains all the data properties.
CODE ANALYSIS
Load and save
The example application in the Ch10 Advanced JavaScript Ch10-10 Fashion Shop No Test Data folder is a version of the Fashion Shop that doesn’t generate test data when first started. You can create and store stock items and they will be persisted when the browser is closed. It uses local storage in exactly the same way as the Tiny Contacts program, but it creates an array of JSON strings to store the data.
We now have all the behaviors that we need to make the Fashion Shop application. We can put all the different kinds of data in objects which can be saved and loaded. Our class-based design means that data properties shared by all the different objects are only stored in one place. The only thing missing is the user interface element. We need a menu system for the application along with views of the different types of data.
We have already built HTML documents that look very similar to parts of the Fashion Shop application. When we created the Tiny Contacts application, we created schema objects to describe the display elements that were needed. We can use the same approach to design HTML elements for each of the data classes in the Fashion Shop. If you’re not sure how we used schemas to design an HTML document, take a look in the “Use a data schema” section in Chapter 9.
static StockItemSchema = [ { id: "price", prompt: "Price", type: "input" }, { id: "stockLevel", prompt: "Stock Level", type: "input" }, { id: "description", prompt: "Description", type: "textarea", rows: 5, cols: 40 }, { id: "color", prompt: "Color", type: "input" }];
The code above shows the display schema for the StockItem
class. This is the same schema design as we used for the Tiny Contacts application. There is an entry for each item to be displayed. The item gives the id
of the property, the prompt to be displayed, and the type of the input. Three of the items are single-line inputs, and one is a text area. If you take a look at Figure 10-5 from earlier in this chapter, you will see how this schema defines the display of the top four values to be entered.
The buildElementsFromSchema
method works through the elements in a schema and uses a function called makeElement
to create each element from the schema information and add it to an HTML element. It is the same mechanism that was used to create the display of the Tiny Contacts application.
getHTML(containerElementId) { StockItem.buildElementsFromSchema(containerElementId, StockItem.StockItemSchema); }
The StockItem
class contains a method called getHTML
that calls buildElementsFromSchema
with the parameters required to build the part of a display needed to edit a StockItem
. Now that we know how to build the display for a StockItem
, we can consider how to build the display for a Dress
.
The code above is the code that builds the display for the dress. The getHTML
method uses a schema that defines just the extra elements that need to be added to the HTML document for the dress. It calls the getHTML
function of its super
object (which is the StockItem
) to get the HTML for that object and then adds its own elements on the end.
CODE ANALYSIS
Creating HTML
A great programming language is one where you can create code that you are proud of. I’m quite proud of this HTML-generating code. It is easy to use and easy to extend. We can add more stock types and easily express what each stock type contains. However, you might have some questions about it.
This application uses four variables that are shared between all the functions:
var mainPage; // HTML element that contains the user interface var dataStore; // Array of stock items var storeName; // name of save data in local storage var activeItem; // currently active stock item (for entry and edit)
When the Fashion Shop starts, these variables must be set up. The body
element of the HTML document containing the application contains an onload
attribute that specifies the function to be called when the page is loaded. This function starts the Fashion Shop running. The function is called doStartFashionShop
.
<body onload="doStartFashionShop('mainPage','fashionShop')" class="mainPage">
The function doStartFashionShop
is very similar to the function doStartTinyContacts
that we created in Chapter 9 to start the Tiny Contacts application. This function needs to load the stock data from the browser and set up the other shared variables.
The last statement in doStartFashionShop
displays the user menu. Lets take a look at how that works.
The user interacts with our program by pressing buttons on the screen that select the various menu options. If you look back to Figure 10-4, you can see the main menu for the display. Each of the program options is selected by pressing a button, and when the button is pressed, the application calls the function for that option.
function doShowMainMenu() { openPage("Main Menu"); showMenu( [{ desc: "Add Dress", label: "Dress", func: "doAddDress()" }, { desc: "Add Pants", label: "Pants", func: "doAddPants()" }, { desc: "Add Skirt", label: "Skirt", func: "doAddSkirt()" }, { desc: "Add Top", label: "Top", func: "doAddTop()" }, { desc: "Update stock item", label: "Update", func: "doUpdateStock()" }, { desc: "List stock items", label: "List", func: "doListFashionShop()" }]); }
I’m using yet another schema to describe each menu option. The function showMenu
works through the schema and builds the display. If you look back to Figure 10-4, you can map the application main menu onto the items in the schema above. The function openPage
removes all the elements on the page and displays a heading.
The menu calls an add
function for each stock item. The doAddDress
function looks like this:
function doAddDress() {
addStock(Dress);
}
It calls the addStock
function and does something we’ve not seen before. It uses the Dress
class as an argument to the call of addStock
. We do this so that we can have a single addStock
function that can create any type of stock item.
The addStock
function makes a new item of the required class (the class is supplied as a parameter). It then creates a new display page and fills it with the HTML generated by the new item. At the end of the page, the function builds a menu containing Save and Cancel buttons. The function handler for the Cancel button just displays the main menu. The function for the Save button copies the inputs from the HTML elements into a new copy of the stock item. This function has the name doSaveAdd
. The job of doSaveAdd
is to copy the data from the HTML document into the currently active item. This item is then stored in the data store.
There is a lot to explore in the Fashion Shop and you can learn a lot by exploring it. The application is heavily based on the Time Tracker application. I would strongly advise you to spend some time going through the code. You can use the Developer View debugger to work through the code as it runs. The great thing about the application is that it is very clear what the intent of each function is. For example, the edit function, which we have not explored in this chapter, must find a stock item to be edited, make that item the active item, and when the edit is completed, copy the edited properties from the HTML document into the data store.
Note that we have not explored the function that provides a list of stock items. You can use it, and you can look at the code that makes it work, but we will be investigating that function and adding some great features to it in the next chapter.
MAKE SOMETHING HAPPEN
Expand the fashion shop
You can also learn a lot about programming by adding features to an existing application. Here are some things that you might like to do with the Fashion Shop application:
Add a new type of clothing called suit
. A suit has the properties jacket size, pant size, color, pattern, and style. You can do this by adding a new class that is a subclass of StockItem
.
Add a new property called manufacturer
, which is to be stored for all the items in stock. You can do this by adding a new attribute to the StockItem
class.
Add some data validation to the application. At the moment the user can save stock item records that have missing data fields. Write a function that tests for empty fields and only allows a record to be saved if all the fields have been filled in.
Create a totally new data storage application, which can store information for another business, perhaps a fishing tackle store. You should find this quite easy to do. You can use the Fashion Shop application as a great starting point.
In this chapter, you learned how JavaScript objects allow programs to store related items as properties of a single object and how to make programs that work with objects.
JavaScript programs can create and throw exception objects that describe an error that has been detected by the code. JavaScript statements that may throw exception objects can be enclosed in a try block as part of a try-catch
construction. The catch
element of the construction contains JavaScript code that only runs in the event of an exception being thrown. This construction allows a program to detect and deal with errors in a managed way.
A program should only raise an exception when something exceptional has occurred. Errors that are to be expected in the normal running of the program (such as invalid user input or network failures) should not be managed using exceptions.
A JavaScript class lets a programmer design the contents of an object by specifying data to be used in the class constructor method to set initial values of properties in the class.
A JavaScript class constructor method accepts parameters that can be used to initialize properties in a newly created class instance.
The JavaScript new
keyword is used to create a new instance of a class by calling a constructor method in that class. Because missing method arguments to a JavaScript method call are replaced by the value undefined
you can use a constructor call with no parameters to create an “empty” class instance that contains undefined values for all the properties.
A JavaScript class can contain methods that are members of the class. A member method can be called by code outside the class to allow an object to provide behaviors for that code.
Within a class method the reference this
refers to the object within which the method is running.
JavaScript inheritance allows the creation of “super” or “parent” classes that can be extended to create “sub” or “child” classes. A subclass contains all the members of the parent. This allows attributes shared by a number of related classes to be stored in a single superclass.
A subclass can override methods in the superclass by providing their own implementations of the method. The keyword super
allows a method in a subclass to call the superclass. The constructor of a subclass must contain a constructor method that makes use of the super
mechanism to initialize the properties of the superclass.
A class can contain static data and method members which are stored as part of the class rather than being part of any instance of a class. A static method is a way that class can provide a behavior or data property that can be used without the need to create an instance of the enclosing class.
Here are some questions that you might like to ponder about what we have learned in this chapter:
What happens if a JavaScript program doesn’t catch an exception that has been thrown?
If an exception is thrown in some JavaScript that is not part of a try-catch
construction the exception will be caught by the browser and the execution sequence will end. If you have the Developer View open, you will see the exception displayed in the form of a red error message.
When should a JavaScript program throw an exception?
Exceptions should only be used in exceptional circumstances. Some JavaScript functions, for example JSON.parse
, use exceptions to signal error conditions. You can also make your code generate exceptions. When you start work on a project you need to decide on all the possible error conditions and then decide how each should be handled. Exceptions are useful because they provide a way that a low-level failure can be quickly propagated to a higher-level error handler.
Must my JavaScript programs catch all exceptions?
No. For me, the biggest concern when I write a program is not that the program might fail. It is that the user might think that it has worked when it has not. If an application fails with an obvious error, the user will get upset. If an application “pretends” that it has worked and the user later finds out that they have lost all their data, they will get very upset. You should ensure that exceptions are logged and reported in a way that makes their error reports useful.
Do I have to use classes in my programs?
No. However, they can make some kinds of programs (particularly those that need to deal with different types of related data) easier to write.
Can a JavaScript class have multiple constructors?
No. Some programming languages have a mechanism called “overloading” where a class can contain multiple versions of a method that all share the same name but have different parameters. JavaScript does not support overloading, so a class can only contain a single constructor method. If you want to provide different ways to construct an object, you must write code in the constructor method to decide what the parameters to the function mean.
What is the difference between a method and a function?
A method is declared as part of a class, whereas a function is declared outside any class. Both can accept parameters and return a result.
Can the this
reference be used as an argument to a function call?
Yes. Within a method, the this
reference refers to the object that the method is running within. If an object wants to send another object a reference to itself, it can pass the value of this
as a function argument. This is like me calling you on the phone and telling you my phone number.
Can I use the this
reference inside a static method?
No. A static method is a member of the enclosing class and is not associated with an existing instance.
23.20.51.162