Exam Objectives
Implement native objects; create custom objects and custom properties for native objects using prototypes and functions; inherit from an object; implement native methods and create custom methods
Quick Overview of Training Materials
Programming in HTML5 with JavaScript and CSS3 - Training Guide - Chapter 6
Developing in HTML5 with JavaScript and CSS3 Jump Start - Module 4
MSDN - JavaScript Fundamentals - Sections on functions and objects
MSDN - Prototypes and Prototype Inheritance
MozDN - Introduction to Object-Oriented JavaScript
JavaScript Closures from Jibbering.com
JavaScript Spec - ECMA 262 v.5
MozDN - Standard built-in objects
Native, Built-in, and Host Objects
The JavaScript specification defines three types of objects:
- native object: object whose semantics are fully defined by the specification rather than by the host environment.
- built-in object: native object that is present at the start of the execution of a program. Standard built-in objects are defined in the specification, and a JavaScript implementation may specify and define others. Every built-in object is a native object. A built-in constructor is a built-in object that is also a constructor.
- host object: object supplied by the host environment to complete the execution environment of JavaScript
Section 15 of the spec defines the built-in objects (links to MSDN or MozDN):
- Global object - holds globally defined symbols (functions and constants)
- Object object - wrapper for name-value pairs
- Function object - block of executable code
- Array object - indexed lists
- String object - sequence of characters
- Boolean object - true/false value
- Number object - numeric value
- Math object - collection of static methods and properties for performing mathematical operations
- Date object - represents a single moment in time
- RegExp object - Regular Expression for pattern matching with strings
- Error object - contain information about runtime errors
- JSON object - methods for parsing and converting to JavaScript Object Notation (JSON)
Host objects are definied by the environment, and include the window and document objects. Getting a handle on what constitutes a non built-in native object was tricky, but it seems that objects created by the user are "native objects" that are not built in. So the code var o = {} creates a native object, because the semantics (for Object objects, which we are creating) are defined in the specification, but the implimentation is defined by the user. The jibbering article describes native objects as "loose and dynamic bags of named properties"
User-defined native objects can be created multiple ways. One way is to use the Object construtor. So we would create a new object thusly:
var MyObj = new Object();
Properties on an object are defined by assigning values to the property name. So if we wanted to add some properties to our new object, we could do so like this:
MyObj.size = "Bigger than a bread box";
MyObj.color = "Yellow";
MyObj.isAlive = true;
Objects can also be create with literal notation, with name: value pairs contained within a pair of braces. Implementing the above object with literal notation would look like this:
var MyObj2 = { size: "Bigger than a bread box",
color: "Yellow",
isAlive: true };
Encapsulation, Inheritance, and Polymorphism
Chapter 6, Lesson 1 in the training guide is invaluable for understanding how to implement Object Oriented Programming in JavaScript. It describes how to implement what it calls the "three pillars of OOP" in JavaScript:
- encapsulation - only expose object members intented for external use, hiding the details that make them work, e.g. in C# or Java creating a private field with public properties (getter and setter)
- inheritance - create parent-child relationships between more general objects (say, Vehicle) and more specific object (Car). In other words, create is-a relationships (Car is a Vehicle).
- polymorphism - Using overriding to change the behavior of inherited methods.
Although JavaScript is object oriented, it does not use classes. Instead, JavaScript is prototype based, i.e. rather than inheriting from a base class, new objects are created from a clone of a prototype object. When assigning values to an undefined property, that property is created on that object. When attempting to read an undefined property, the JavaScript interpreter will work it's way up the prototype chain until it either finds a defined value for the property, or until it reaches the null object (i.e. the end of the prototype chain).
The mechanism to creating classes in JavaScript is to use functions. Within a function, it is possible to use var and this to assign data and functions to an object. The var keyword will make these data private, while those defined with this will be exposed publically. So if we wanted to create an class for vehicles, rather than creating a single object with make, model, and year defined like such:
var vehicle = {make: "Ford", model: "F-150", year: 1987}
... which would have to be repeated for every single vehicle we wanted to define, we could use a function to create a constructor:
function vehicle(makeParam, modelParam, yearParam){
var make = makeParam;
var model = modelParam;
var year = yearParam;
this.getMake = function () { return make; };
this.getModel = function () { return model;};
this.getYear = function () { return year; };
}
Calling this function with the "new" keyword will create a new instance of vehicle object. The make, model, and year variables are private to the object, and can be accessed using the exposed getter functions getMake, getModel, and getYear.
var vehicle = (function () {
function vehicle(makeParam, modelParam, yearParam){
var make = makeParam;
var model = modelParam;
var year = yearParam;
this.getMake = function () { return make; };
this.getModel = function () { return model;};
this.getYear = function () { return year; };
}
var automobile = (function (parent) {
automobile.prototype = new vehicle();
automobile.prototype.constructor = automobile;
function automobile(makeParam, modelParam, yearParam, tiresizeParam)
{
parent.call(this, makeParam, modelParam, yearParam);
var tiresize = tiresizeParam;
this.getTiresize = function () {return tiresize};
}
automobile.prototype.getInfo = function () {
return parent.prototype.getInfo.call(this) + ' ' + this.getTiresize();
};
return automobile;
})(vehicle);
Now we can get even more specific by creating a "truck" class that includes towing capacity:
var truck = (function (parent) {
truck.prototype = new automobile();
truck.prototype.constructor = truck;
function truck(makeParam, modelParam, yearParam, tiresizeParam, towingParam)
{
parent.call(this, makeParam, modelParam, yearParam, tiresizeParam);
var towing = towingParam;
this.getTowing = function () {return towing};
}
truck.prototype.getInfo = function () {
return parent.prototype.getInfo.call(this) + ' ' + this.getTowing();
};
return truck;
})(automobile);
Very little actually changed between the definition of truck and automobile, but truck still inherited all the properties and methods from both vehicle and automobile. These classes also demonstrate polymorphism, as the getInfo method is redefined (overridden) in each of the child classes so that their complete information is displayed:
These examples were taken pretty much verbatim out of the training guide. The Mozilla Dev Net article presents pretty much the same material but uses person-student as the parent-child relationship (I've modified it here a bit):
In case I ever want to copy/paste and play around with these again:
var person = (function () { function person(genderParam, weightParam){ if(genderParam == "Male" || genderParam == "Female") var gender = genderParam; var weight = weightParam; this.getWeight = function () {return weight;}; this.setWeight = function (w) {if(w>0) weight = w; else weight = 0;}; this.getGender = function () {return gender;}; this.setGender = function (gen) {if(gen == "Male" || gen == "Female") gender = gen; else console.log("Must be 'Male' or 'Female'");}; } person.prototype.walk = function () {console.log('I am taking a walk!');}; person.prototype.sayHello = function () {console.log('Hi');}; person.prototype.getSexChange = function () {if(this.getGender() == "Male") this.setGender("Female"); else this.setGender("Male");}; person.prototype.goodDiet = function () {console.log("Lost 10 lbs!"); this.setWeight(Number(this.getWeight() - 10));}; return person; })();
var student = (function (parent) { student.prototype = new parent(); student.prototype.constructor = student; function student(sex, wt, gpaParam) { parent.call(this, sex, wt); var gpa = gpaParam; this.getGpa = function () {return gpa;}; this.setGpa = function (gpaP) { gpa = gpaP; console.log("GPA now = " + gpaP);}; } return student; })(person);
The mechanism to creating classes in JavaScript is to use functions. Within a function, it is possible to use var and this to assign data and functions to an object. The var keyword will make these data private, while those defined with this will be exposed publically. So if we wanted to create an class for vehicles, rather than creating a single object with make, model, and year defined like such:
var vehicle = {make: "Ford", model: "F-150", year: 1987}
... which would have to be repeated for every single vehicle we wanted to define, we could use a function to create a constructor:
function vehicle(makeParam, modelParam, yearParam){
var make = makeParam;
var model = modelParam;
var year = yearParam;
this.getMake = function () { return make; };
this.getModel = function () { return model;};
this.getYear = function () { return year; };
}
Calling this function with the "new" keyword will create a new instance of vehicle object. The make, model, and year variables are private to the object, and can be accessed using the exposed getter functions getMake, getModel, and getYear.
If we want to make a function available to every instance of vehicle without actually defining it as part of the vehicle class (which creates additional memory overhead), we can define it as part of the prototype:
vehicle.prototype.getInfo = function () {
return this.getMake() + ' ' + this.getModel() + ' ' + this.getYear();
return this.getMake() + ' ' + this.getModel() + ' ' + this.getYear();
}
Now we can create multiple instances of vehicle and all will have access to the getInfo() function:
Another way to create a class is by wrapping the defining function in parenthesis, creating what is called an immediately invoked function expression (IIFE). Creating an IIFE causes the interpreter to define the class and then immediately execute it, which is useful for creating namespaces. It is also useful for inheritance, as it allows a child class to accept the parent class as a parameter. Consider if we want to make a more generalized version of vehicle, and then implement a child class "automobile" that includes tire diameter, and a child of automobile called "truck" that includes towing capacity. Lets start with vehicle:
function vehicle(makeParam, modelParam, yearParam){
var make = makeParam;
var model = modelParam;
var year = yearParam;
this.getMake = function () { return make; };
this.getModel = function () { return model;};
this.getYear = function () { return year; };
}
vehicle.prototype.getInfo = function () {
return this.getMake() + ' ' + this.getModel() + ' ' + this.getYear();
return this.getMake() + ' ' + this.getModel() + ' ' + this.getYear();
}
return vehicle;
})();
Next we define the car class. It will need to call the constructor of the parent class with the passed parameters, and then we can add automobile specific properties. Inheritance is accomplised by setting the prototype of automobile to a new vehicle object, and setting the contructor to the automobile function:
automobile.prototype = new vehicle();
automobile.prototype.constructor = automobile;
function automobile(makeParam, modelParam, yearParam, tiresizeParam)
{
parent.call(this, makeParam, modelParam, yearParam);
var tiresize = tiresizeParam;
this.getTiresize = function () {return tiresize};
}
automobile.prototype.getInfo = function () {
return parent.prototype.getInfo.call(this) + ' ' + this.getTiresize();
};
return automobile;
})(vehicle);
Now we can get even more specific by creating a "truck" class that includes towing capacity:
var truck = (function (parent) {
truck.prototype = new automobile();
truck.prototype.constructor = truck;
function truck(makeParam, modelParam, yearParam, tiresizeParam, towingParam)
{
parent.call(this, makeParam, modelParam, yearParam, tiresizeParam);
var towing = towingParam;
this.getTowing = function () {return towing};
}
truck.prototype.getInfo = function () {
return parent.prototype.getInfo.call(this) + ' ' + this.getTowing();
};
return truck;
})(automobile);
Very little actually changed between the definition of truck and automobile, but truck still inherited all the properties and methods from both vehicle and automobile. These classes also demonstrate polymorphism, as the getInfo method is redefined (overridden) in each of the child classes so that their complete information is displayed:
In case I ever want to copy/paste and play around with these again:
var person = (function () { function person(genderParam, weightParam){ if(genderParam == "Male" || genderParam == "Female") var gender = genderParam; var weight = weightParam; this.getWeight = function () {return weight;}; this.setWeight = function (w) {if(w>0) weight = w; else weight = 0;}; this.getGender = function () {return gender;}; this.setGender = function (gen) {if(gen == "Male" || gen == "Female") gender = gen; else console.log("Must be 'Male' or 'Female'");}; } person.prototype.walk = function () {console.log('I am taking a walk!');}; person.prototype.sayHello = function () {console.log('Hi');}; person.prototype.getSexChange = function () {if(this.getGender() == "Male") this.setGender("Female"); else this.setGender("Male");}; person.prototype.goodDiet = function () {console.log("Lost 10 lbs!"); this.setWeight(Number(this.getWeight() - 10));}; return person; })();
var student = (function (parent) { student.prototype = new parent(); student.prototype.constructor = student; function student(sex, wt, gpaParam) { parent.call(this, sex, wt); var gpa = gpaParam; this.getGpa = function () {return gpa;}; this.setGpa = function (gpaP) { gpa = gpaP; console.log("GPA now = " + gpaP);}; } return student; })(person);
No comments:
Post a Comment