Rabu, 29 September 2010

Learn Object Oriented Basics of Javascript Designing ans Tutorial

Thanks to expertly crafted libraries such as jQuery and MooTools, JavaScript has become a foundation of Front-End Development. However, it’s extremely important that we note the higher level concepts utilized in these wonderful libraries. Why is that? As web developers, we must equally balance learning the current programming trends with attempts to push the boundaries of those trends. Without this balance, no innovation will occur in web development. So let’s take a moment to appreciate the fundamentals of JavaScript Object Oriented Programming, which include classes, inheritance, and scope.

Classes

Before we learn how to implement classes into your code, let’s discuss what classes are and why you should bother learning/using them.
As stated in the Java documentation, “A class is the blueprint from which individual objects are created.” Consider an actual blueprint used in the process of making a house. The builders use the blueprint to evaluate what properties the house contains and what functions the house will execute. Classes are a convenient way to represent the properties of objects, be it a house, car, or person. Classes become especially useful when more than one of a given object exists.
As an example, let’s compare two real-life objects without the use of classes. This mirrors the procedural thought process as opposed to the object oriented thought process. We’ll describe a man named Rob and a baby girl named Emillee. We must assume we know nothing about the human body since we have no blueprint(class) to work from.
Rob:
  1. Rob has two oval-like structures a few inches apart on the upper portion of his body. These oval like structures have a white background with a brown middle.
  2. Rob has two structures that run relatively parallel to the floor that seem to indicate the most vertical portion of the body that’s still part of the body’s base.
  3. Rob has two appendages that extend from another two appendages. These seem to be used to grab items. They seem relatively large.
  4. Rob measures approximately 6 feet in height.
  5. Rob involuntarily intakes oxygen and converts it into Carbon Dioxide.
Emilee:
  1. Emillee has two oval-like structures a few inches apart on the upper portion of her body. These oval like structures have a white background with a blue middle.
  2. Emillee has two structures that run relatively parallel to the floor that seem to indicate the most vertical portion of the body that’s still part of the body’s base.
  3. Emillee has two appendages that extend from another two appendages. These seem to be used to grab items. They seem relatively small.
  4. Emillee measures approximately 1.5 feet in height.
  5. Emillee involuntarily intakes oxygen and converts it into Carbon Dioxide.
That was a LOT of work to just describe a human’s 1) Eyes, 2) Shoulders, 3) Hands, 4) Height, and 5) Act of Breathing. Notice we had to make almost the exact same observations both times since we had no blueprint to work from. While describing 2 people wasn’t too bad, what if we wanted to describe 100, 1000, a million? There certainly has to be a more efficient way of describing objects that have similar properties: This is where classes shine.
Let’s rethink the previous example using an object oriented mindset. Since we are describing a man and a baby girl, we know they are both humans. So let’s first create a simple blueprint for humans.
Humans:
  1. Have two oval-like structures a few inches apart on the upper portion of his body. These oval like structures have a white background with a varying color in the middle. We call them eyes.
  2. Have two structures that run relatively parallel to the floor that seem to indicate the most vertical portion of the body that’s still part of the body’s base. We call them shoulders.
  3. Have two appendages that extend from another two appendages. These seem to be used to grab items. Their size varies. We call them hands.
  4. Vary in height depending on age and other factors. We call this height.
  5. Involuntarily intake oxygen and convert it into Carbon Dioxide. We call this breathing.
So we’ve stated that the properties of humans are that they have eyes, shoulders, hands, and a height. We’ve also stated that these properties may vary. Having defined the blueprint of a Human, and having declared that Rob and Emillee are human, we can apply what we already know about humans to Rob and Emillee.
Rob is a Human.
  1. Rob has brown eyes
  2. Rob has shoulders
  3. Rob has large hands
  4. Rob is 6 feet tall
  5. Rob breathes
Emillee is a Human.
  1. Emillee has blue eyes
  2. Emillee has shoulders
  3. Emillee has small hands
  4. Emillee is 1.5 feet tall
  5. Emillee breathes
By explicitly stating Rob and Emillee are Human, we take the properties and functions associated with being Human and apply them directly to Rob and Emillee. This lets us avoid redefining all the body parts while also letting us effectively describe the key differences between the two objects.
Here are a few examples of classes and their objects(known as instances of the class) so you understand the relationship between the two.
Class Student
  • Properties: grades, age, date of birth, SSID
  • Functions: calculate GPA, view absences, update conduct
Class Employee
  • Properties: EIN, hourly wage, contact number, insurance
  • Functions: set wage, view productivity, pull resume
Class Computer
  • Properties: CPU, motherboard, monitor
  • Functions: turn on, turn off, restart
So now that we understand the idea behind classes, let’s apply what we know to JavaScript. Unlike languages including PHP and C++, JavaScript does not have the class data type. However, using JavaScripts flexibility, we can easily mimic a class using functions.
Referring to one of our previous examples, let’s use a class to represent students.
When creating a class, there are two things you must do: You must know what properties/functions(also known as methods) the class will have, and you need to initialize the properties with a value.
01function Student(name, gender, age, grade, teacher)
02{
03    this.name = name;
04    this.gender = gender;
05    this.age = age;
06    this.grade = grade;
07    this.teacher = teacher;
08}
09 
10var bob = new Student("bob", "male", 15, 10, "Marlow");
11alert(bob.age); //Outputs 15
12 
13var susan = new Student("susan", "female", 10, 5, "Gresham");
14alert(susan.gender); //Outputs 'female'
From this example we can see that instances of the class are initiated using the new operator. Properties and methods of the class are accessed using the . (dot) operator. So to get the property age of the instance of the Student class named bob, we simply use bob.age. Similarly, we created an instance of the Student class and assigned that to susan. To get the gender of susan, we simply use susan.gender. The code readability benefits from classes are enormous: You can reason that bob.age is the age of bob without having any programming experience.
However, the previous example contains two detrimental (but easily fixable) flaws.
1) The class properties can be accessed by any statement
2) The arguments must be passed in a certain order

Keeping property values private

Note that in the previous example, we were able to get the value of bob.age by simply calling bob.age. Additionally, we could set bob.age to any value we feel like anywhere in the program.
1var bob = new Student("bob", "male", 15, 10, "Marlow");
2alert(bob.age); //Outputs 15
3 
4bob.age = 9;
5alert(bob.age); //Outputs 9;
Seems harmless, right? Well, consider this example.
1var bob = new Student("bob", "male", 15, 10, "Marlow");
2alert(bob.age); //Outputs 15
3 
4bob.age = -50;
5alert(bob.age); //Outputs -50;
We have age as a negative number: A logical inconsistency. We can prevent issues like this and preserve the integrity of our data by utilizing the concept of private variables. A private variable is a variable that can only be accessed within the class itself. While once again JavaScript does not have a reserve word for making a variable private, JavaScript gives us the tools to create the same effect.
01function Student(name, gender, age, grade, teacher)
02{
03    var studentName = name;
04    var studentGender = gender;
05    var studentGrade = grade;
06    var studentTeacher = teacher;
07    var studentAge = age;
08 
09    this.getAge = function()
10    {
11        return studentAge;
12    };
13 
14    this.setAge = function(val)
15    {
16        studentAge = Math.abs(val); //Keep age positive using absolute value
17    };
18}
19 
20var bob = new Student("bob", "male", 15, 10, "Marlow");
21alert(bob.studentAge); //undefined, since age is privately protected in the class definition
22 
23alert(bob.getAge()); //Outputs 15
24bob.setAge(-20);
25alert(bob.getAge()); //Outputs 20
By using variable declarations as opposed to attributing properties directly to the class, we have protected the integrity of our age data. Since JavaScript utilizes function scope, a variable declared within our class will not be made accessible outside of that class unless explicitly returned by a function within the class. The method this.getAge, which returns the student age to the calling environment, is known as an Accessor method. An Accessor method returns the value of a property so that the value can be used outside the class without affecting the value inside the class. Accessor methods are usually preceded with the word “get” by convention. The method this.setAge is known as a Mutation method. Its purpose is to alter the value of a property and preserve its integrity.
So we see the benefits of using Accessor and Mutation methods within a class to preserve the integrity of data. However, creating an Accessor method for each property creates extremely long-winded code.
01function Student(name, gender, age, grade, teacher)
02{
03    var studentName = name;
04    var studentGender = gender;
05    var studentGrade = grade;
06    var studentTeacher = teacher;
07    var studentAge = age;
08 
09    this.getName = function()
10    {
11        return studentName;
12    };
13 
14    this.getGender = function()
15    {
16        return studentGender;
17    };
18 
19    this.getGrade = function()
20    {
21        return studentGrade;
22    };
23 
24    this.getTeacher = function()
25    {
26        return studentTeacher;
27    };
28 
29    this.getAge = function()
30    {
31        return studentAge;
32    };
33 
34    this.setAge = function(val)
35    {
36        studentAge = Math.abs(val); //Keep age positive using absolute value
37    };
38}
39 
40var bob = new Student("bob", "male", 15, 10, "Marlow");
41alert(bob.studentGender); //undefined, since gender is privately protected in the class definition
42 
43alert(bob.getGender()); //Outputs 'male'
My C++ Professor always said “If you find yourself typing the same code over and over, you’re doing it wrong.” Indeed there is a more efficient way to create Accessor methods for each property. Additionally, this mechanism also eliminates the need to call function arguments in a specific order.

Dynamically Generated Accessor methods

This demonstration is based off John Resig’s Pro JavaScript Techniques book (which I highly encourage you to read. The first 3 chapters alone are worth it).
01function Student( properties )
02{
03    var $this = this//Store class scope into a variable named $this
04 
05    //Iterate through the properties of the object
06    for ( var i in properties )
07    {
08 
09        (function(i)
10        {
11            // Dynamically create an accessor method
12            $this[ "get" + i ] = function()
13            {
14                return properties[i];
15            };
16        })(i);
17    }
18}
19 
20// Create a new user object instance and pass in an object of
21// properties to seed it with
22var student = new Student(
23{
24    Name: "Bob",
25    Age: 15,
26    Gender: "male"
27});
28 
29alert(student.name); //Undefined due to the property being private
30 
31alert(student.getName()); //Outputs "Bob"
32alert(student.getAge()); //Outputs 15
33alert(student.getGender()); //Outputs "male"
By implementing this technique, not only do we keep our properties private, we avoid the need to specify our arguments in order. The following class instantiations are all equivalent
01var student = new Student(
02{
03    Name: "Bob",
04    Age: 15,
05    Gender: "male"
06});
07 
08var student = new Student(
09{
10    Age: 15,
11    Name: "Bob",
12    Gender: "male"
13});
14 
15var student = new Student(
16{
17    Gender: "male",
18    Age: 15,
19    Name: "Bob"
20});

Inheritance

Recall that throughout this article I’ve used the term “class” extremely loosely. As stated before, JavaScript has no class entity, but the pattern of classes can still be followed. The main difference between JavaScript and other object oriented languages lies in their inheritance models. C++ and Java exhibit Class-based or “Classical” inheritance. JavaScript, on the other hand, exhibits Prototypal Inheritance. In other object oriented languages, class is an actual data type that represents the blueprint for creating objects. In JavaScript, although we can use Functions to simulate an object blueprint, they are just in fact objects themselves. These objects are then used as models (aka prototypes) for other objects.(See Article JavaScript Prototypal Inheritance).
Applying the concept of prototypal inheritance allows us to create “subclasses”, or objects that inherit the properties of another object. This becomes particularly useful when we want to use the methods of another object with some slight modifications.
Consider a class Employee. Let’s say we have two types of employees: wage-based and commission-based. These employee types will have many similar properties. For example, regardless of whether an employee receives income through commission or receives income through wages, an employee will have a name. However, the way the income for a commission-based employee and a wage-based employee is completely different. The following example captures this idea.
01function Worker()
02{
03    this.getMethods = function(properties, scope)
04    {
05        var $this = scope;  //Store class scope into a variable named $this
06 
07        //Iterate through the properties of the object
08        for ( var i in properties )
09        {
10 
11            (function(i)
12            {
13                // Dynamically create an accessor method
14                $this[ "get" + i ] = function()
15                {
16                    return properties[i];
17                };
18 
19            //Dynamically create a mutation method that parses for an integer and
20            //Ensures it is positive.
21            $this[ "set" + i ] = function(val)
22            {
23                if(isNaN(val))
24                {
25                    properties[i] = val;
26                }
27                else
28                {
29                    properties[i] = Math.abs(val);
30                }
31            };
32            })(i);
33        }
34    };
35}
36 
37//The CommissionWorker "subclass" and WageWorker "subclass"
38//inherit the properties and methods of Worker.
39CommissionWorker.prototype = new Worker();
40WageWorker.prototype = new Worker();
41 
42function CommissionWorker(properties)
43{
44    this.getMethods(properties, this);
45 
46    //Calculates income
47    this.getIncome = function()
48    {
49        return properties.Sales * properties.Commission;
50    }
51}
52 
53//Expects the following properties: wage, hoursPerWeek, weeksPerYear
54function WageWorker(properties)
55{
56    this.getMethods(properties, this);
57 
58    //Calculates income
59    this.getIncome = function()
60    {
61        return properties.Wage * properties.HoursPerWeek * properties.WeeksPerYear;
62    }
63}
64 
65var worker = new WageWorker(
66{
67    Name: "Bob",
68    Wage: 10,
69    HoursPerWeek: 40,
70    WeeksPerYear: 48
71});
72 
73alert(worker.wage); //Undefined. wage is a private property.
74 
75worker.setWage(20);
76alert(worker.getName());   //Outputs "Bob"
77alert(worker.getIncome()); //Outputs 38,400 (20*40*48)
78 
79var worker2 = new CommissionWorker(
80{
81    Name: "Sue",
82    Commission: .2,
83    Sales: 40000
84});
85alert(worker2.getName());   //Outputs "Sue"
86alert(worker2.getIncome()); //Outputs 8000 (2% times 40,000)
The most important statements from the previous example are:
1CommissionWorker.prototype = new Worker();
2WageWorker.prototype = new Worker();
This states that for each instance of a new CommissionWorker or a new WageWorker object, the properties and methods of Worker will be passed down to those new objects. These methods and properties can be overwritten within the “subclass” definition if so desired.

Scope

JavaScript exhibits what is known as function scope. This means variables declared in a function are not initially accessible outside the function from which they originate. However, in blocks (such as coniditonal statements), variable declarations or alterations are made available to the calling environment.
01var car = "Toyota";
02 
03if(car == "Toyota")
04{
05    car = "Toyota - We never stop...and you won't either.";
06}
07 
08alert(car); //Ouputs Toyota - We never stop...and you won't either.
09 
10car = "Toyota"; //Reset car back to original value.
11 
12function makeFord(car)
13{
14    car = "Ford";
15}
16 
17makeFord(car);
18alert(car); //Outputs "Toyota" because car was altered in the function scope.
However, if you want a function to alter the value, you can pass in an object as an argument and alter a property of the object.
01var car = new Object();
02car.brand = "Toyota"
03 
04function makeFord(car)
05{
06    car.brand = "Ford";
07}
08 
09makeFord(car);
10 
11alert(car.brand); //Outputs "Ford"
This is known as passing a value to a function “by reference”. I generally recommend passing by reference only if you are setting up methods within a class and you know what properties the object will contain.
You are now armed with the object oriented basics as applied to JavaScript. Use these principles to simplify your code for projects in the future.

Tidak ada komentar:

Posting Komentar