Learning notes on Javascript class/objects

in Javascript a "class" is "function", function body is the "constructor"

function className() {
   // constructor
}
// create an instance of className
var obj = new className();
// Object is predefined
new Object();
// Use literal notation:
var obj = {
     propData: 100,
     propString: "hello world",
     propArray: ["a", "bbb", "", "jajajaja"],
     methodA: function () { ... },
     methodB: function () {... },
     multilineString: '<div class="pp_pic_holder"> \
      <div class="ppt">&nbsp;</div> \
      <div class="pp_top"> \
      <div class="pp_left"></div>';
};

object literial notation

This is the fastest way to creat a global object. The literial notation is not a "class", it can only be used to create one instance.

var Me = {
    name: "Robert Mao",
    sex: "M",
    say: function() {
        alert("hello world");
    }
};
// works
Me.say();
 
// ERROR: Me is not a constructor
var me = new Me();
 
Me.run = function() {
    alert("I can run");
}
 
Me.run();
 
// WRONG!!!
Me.prototype.runMore = function() {}

Factory method (by return literial notation)

factory method actually return an object created by literial notation. it can be used to create some "private" member of a object.

function createObject(name) {
    var privateName = name || "I am private name";
    var privateMethod = function () {
        return "result of private";
    }
    return {
        publicName: "I am public name",
        publicMethod: function () {
            alert("privateName is " + privateName + ", privateMethod called: "+ privateMethod());
        }
    }
}
 
var obj2 = createObject("robert");
obj2.publicMethod();

Function constructor

function Person(name) {
    var privateName = name || "I am private";
    var privateMethod = function() {
        return privateName;
    }
 
    this.publicName = "I am public";
    this.publicMethod = function() {
        alert(privateName + " get from public method!");
    }
}
 
var obj3= new Person("robert");
obj3.publicMethod();

(public) class member

 
function Person(name) {
    //  access the "private" members
    //  is actually add member to this instance only
    this.method = function() { }
}
// will not able access "private" members defined in the constructor
// extend all the instance of such class and Impact to the class inherit
function.prototype.newMethod = function() {
}

inherit

function A() {
    this.method = function() { }
}
 
function B() {
    var base = A;
    // call base constructor
    base();
}
// inheritance
B.prototype = new A();
var obj = new B();
// ok inherit from class A
obj.method();

method overwrite

   function B() {
      this.method = function() {
      // overwrite the "method" defined in base class
      }
   }
this.method = function() {
    // overwrite the "method" defined in base class
    this.__proto__.method.call(this);
}
 
// sets the context to the body element
// with the current arguments
myfunct.apply(document.body, arguments);

using prototype to create such method, it will looks more neat:

function A() { }
 
A.prototype.method = function() { }
 
function B() { }
 
B.prototype = new A();
 
B.prototype.method = function() {
    // call old "method" defined in class A
    A.prototype.method.call(this);
}

If you are creating a class that might be subclassed later, it is best to stick to one of the fully exposed patterns. var Book = function(newIsbn) { implements Publication

// Private attributes. var isbn;

// Private method, not possible to override it function checkIsbn(isbn) { }

// Privileged methods: can access private vars this.getIsbn = function() { return isbn; }; this.setIsbn = function(newIsbn) { if(!checkIsbn(newIsbn)) throw new Error('Book: Invalid ISBN.'); isbn = newIsbn; };

// Constructor code. this.setIsbn(newIsbn);

};

// Public, non-privileged methods. Book.prototype = { display: function() { } };

By putting the private members in a closure, you are ensuring that they will never be used outside of the object. You have complete freedom to change the implementation details without breaking anyone else's code.

var Book = (function() {
        // Private static attributes.
        var numOfBooks = 0;
 
        // Private static method.
        function checkIsbn(isbn) {
            ...
        }
 
        // Return the public constructor.
        return function(newIsbn, newTitle, newAuthor) { // implements Publication
            // Private attributes.
            var isbn, title, author;
            // Privileged methods.
            this.getIsbn = function() {
                return isbn;
            };
            this.setIsbn = function(newIsbn) {
                if(!checkIsbn(newIsbn)) throw new Error('Book: Invalid ISBN.');
                isbn = newIsbn;
            };
            this.getTitle = function() {
                return title;
            };
            this.setTitle = function(newTitle) {
                title = newTitle || 'No title specified';
            };
            this.getAuthor = function() {
                return author;
            };
            this.setAuthor = function(newAuthor) {
                author = newAuthor || 'No author specified';
            };
            // Constructor code.
            numOfBooks++; // Keep track of how many Books have been instantiated
            // with the private static attribute.
            if(numOfBooks > 50)
                throw new Error('Book: Only 50 instances of Book can be created.');
            this.setIsbn(newIsbn);
            this.setTitle(newTitle);
            this.setAuthor(newAuthor);
        }
})();
 
// function execute immediately, as soon as the code is loaded (not when the Book constructor is called).
// The result of that execution is another function, which is returned and set to be the Book con-
// structor. When Book is instantiated, this inner function is what gets called;
 
// Public static method.
Book.convertToTitleCase = function(inputString) {
};
 
// Public, non-privileged methods.
Book.prototype = {
    display: function() {
        ...
    }
};

inheritance

The Prototype Chain

To create a class that inherits from Person, it gets a little more complex:

function Author(name, books) {
  Person.call(this, name); // Call the superclass's constructor in the scope of this.
   this.books = books; // Add an attribute to Author.
}
Author.prototype = new Person(); // Set up the prototype chain.
Author.prototype.constructor = Author; // Set the constructor attribute to Author.
Author.prototype.getBooks = function() { // Add a method to Author.
   return this.books;
};
 
/* Extend function. */
function extend(subClass, superClass) {
  var F = function() {};
  F.prototype = superClass.prototype;
  subClass.prototype = new F();
  subClass.prototype.constructor = subClass;
}

Prototypal inheritance

/* Clone function. */
function clone(object) {
    function F() {}
    F.prototype = object;
    return new F;
}

Prototypal inheritance is very memory-efficient. Because of the way prototype chain reads members, all cloned objects share a single copy of each attribute and method, until those attrib- utes and methods are written to directly on the cloned object. Contrast this with the objects created using classical inheritance, where each object has a copy of every attribute (and pri- vate method) in memory.

var Person = {
   name: 'default name',
   getName: function() {
     return this.name;
   }
};
/* Author Prototype Object. */
var Author = clone(Person);
Author.books = []; // Default value.
Author.getBooks = function() {
  return this.books;
}

The methods and attributes of this clone can then be overridden. You can change the default values given by Person, or you can add new attributes and methods. That creates a new prototype object, which you can then clone to create new Author-like objects:

var author = [];
author[0] = clone(Author);
author[0].name = 'Dustin Diaz';
author[0].books = ['JavaScript Design Patterns'];
author[1] = clone(Author);
author[1].name = 'Ross Harmes';
author[1].books = ['JavaScript Design Patterns'];
author[1].getName();
author[1].getBooks();