2
\$\begingroup\$

I'm a budding JavaScript programmer, working on an exercise where I'm trying to create two classes (Books and Shelf) and need to have a shelf know what books are on it. I'm pretty new to OOP so I know I'm missing a crucial step (or three). I probably already ran off the rails, but any point in the right direction would be greatly appreciated.

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Library</title>
</head>

<body>

<script type="text/javascript">

//The Books
function Books(title, enshelf, unshelf) {
    this.title = title;
    this.enshelf = enshelf;
    this.unshelf = unshelf;
}

var bookA = new Books('The War of 1812',true,false);
var bookB = new Books('Watership Down',false,true);
var bookC = new Books('Sand Cake',true,false);

//The Shelves
function Shelf (shelfNumber, shelfName) {
    this.shelfNumber = shelfNumber;
    this.shelfName = shelfName;
}

var shelf100 = new Shelf('100','History');
var shelf200 = new Shelf('200','Fiction');
var shelf300 = new Shelf('300','Childrens');

var shelfTotal = [shelf100, shelf200, shelf300];
var bookInventory = [bookA, bookB, bookC];

// List all the books in the library
function displayBooks(book) {
    document.write(book.title + "<br>" + book.shelfName +"<br><br>");
}

//Show total number of books
var showBooks = function() {
    for(i=0; i < bookInventory.length; i++) {
        displayBooks(bookInventory[i]);
    }
}

showBooks();
document.write("Total number of books: " + bookInventory.length);

</script>

</body>
</html>
\$\endgroup\$
0

2 Answers 2

3
\$\begingroup\$

There are multiple ways of handling this. If you can pass the Books you want to the Shelf object, that would be probably one of the easiest methods.

//The Shelves
function Shelf (shelfNumber, shelfName) {
    this.shelfNumber = shelfNumber;
    this.shelfName = shelfName;
    this.books = new Array();
    this.addBook = function(book) {
        this.books[this.books.length] = book;
    }
    this.displayBooks = function() {
        for(i=0; i < this.books.length; i++) {
            displayBooks(this.books[i]);
        }
    }
}

// then you can use it like this...

shelf100.addBook(bookA);
shelf100.addBook(bookB);
shelf100.addBook(bookC);

shelf100.displayBooks();
\$\endgroup\$
0
2
\$\begingroup\$

First off, naming. Objects tend to be singular in form. Thus, the names of your objects should be singular in form. You should name them Book and Shelf, since there's one book per Book instance, and one shelf per Shelf instance.

You got the constructors right, with a bit of correction needed.

A book only needs to care about itself. Thus, only book data should be in the instance, like the title:

function Book(title) {
  this.title = title;

  //explained later
  this.currentShelf;
}

var book = new Book('The War of 1812');

The shelf concerns about itself, and the books it contains. Basically, if you look at the shelf, it's the one that "manages" the books inside it. You could have the book identify where it is, but you should delegate the operations to one of the objects instead of have them in both. I'll explain in the following sections, under methods.

function Shelf (number, name) {
  this.books = [];
  this.number = number;
  this.name = name;
}

var shelf = new Shelf(1,'History');

Now, operations of books and shelves. In prototypal OOP, everything is public. Thus, you don't need setters and getters unless you need intermediate operations like validation.

shelf.books.push(book); //adding a book
shelf.books[i]          //should be book

If you want to declare methods for accessing and operating, the formal way in prototypal OOP is to declare it in the prototype. This has the advantage of being shared across instances. This means that instances use the same, single function and not one function each. This saves memory.

Shelf.prototype.addBook = function(book){
  //this refers to the instance
  this.books.push(book)
}

Shelf.prototype.getBook = function(i){
  return this.books[i];
}

//all instances of Shelf have an addBook
shelf.addBook(book);

//all instances of Shelf have a getBook
shelf.getBook(0);

Now back to the part where book should know where it is. Think of it in a heirarchy, shelves manage book. But book needs to know where it is, so we set addBook to set it for us. Note that addBook is a method of Shelf. And so, redefining addBook:

Shelf.prototype.addBook = function(book){
  //assigning the book the number of the current shelf it's in
  book.currentShelf = this.number;
  //you might search the array for book before adding it to avoid duplicate
  this.books.push(book);
}

So what we did up there is have shelf add the book, as well as additional info. getBook could also do the same thing with de-shelfing.

Shelf.prototype.getBook = function(i){
  var book = this.books.splice(i,1)[0]; //remove a book from the array
  if(!book) return;                     //book at index might not exist
  book.currentShelf = null;             //indicate book not in a shelf
  return book;
}

For a collection of shelves, you can create another object similar to shelves which collect shelves, like a Library object. This object would only concern itself and it's collection of shelves.

As for displaying the shelves, this is a job for the View. You should read more about MVC to properly segregate your code into concerns. That way, your code won't be a mangled mess when it grows.

That's pretty much all the formal OOP I can dish out.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.