Javascript ve Nesneye Dayalı Programlama
Principles of Object-Oriented Programming in JavaScript
Principles of Object-Oriented Programming in JavaScript
Abstract Data Types
Abstract data types are descriptions and representations of a real-world construct. Many languages support aggregation of disparate intrinsic data types to represent some real world construct. For example in "C" the struct keyword denotes such an aggregation. However an abstract data type is more than just an aggregation of disparate data types. An abstract data type also defines behaviors represented by the abstraction. In many object-oriented languages the combination of data and its associated behaviors denotes a class. Languages such as C++, Java, and C# provide the class keyword to identify abstract data types.
While JavaScript reserves the class keyword it does not support defining classes in the same manner as conventional OOP languages. In JavaScript functions are used as object descriptors and all functions are actually JavaScript objects. Thus in JavaScript, classes are function definitions. The code below illustrates how the simplest class is defined in JavaScript -- the empty class MyClass:
Using the JavaScript new operator defines myClassObj as an object instance of the type MyClass:
Notice that the function keyword is overloaded and serves as both the constructor function for objects as well as identifying procedural functions:
The difference between MyClass being interpreted as a constructor or as a procedure is the new operator. The new operator instantiates an object of class MyClass calling the constructor function, while in the second call a procedural call is made to the function MyClass expecting a return result.
MyClass defined in figure 1 is an empty class having no assigned data or behavior. JavaScript can dynamically add properties to the instantiated objects of MyClass:
myClassObj.myData = 5;
myClassObj.myString = "Hello World";
alert( myClassObj.myData ); // displays: 5
alert( myClassObj.myString ); // displays: "Hello World"
The problem however is that only the instance of MyClass referenced by myClassObj possesses the additional data properties. Subsequent instances will not have any properties. What is needed is a way to defined properties to all instances of MyClass. Using the this keyword in the constructor function data properties are now defined on all instances of MyClass:
{
this.myData = 5;
this.myString = "Hello World";
}
var myClassObj1 = new MyClass();
var myClassObj2 = new MyClass();
myClassObj1.myData = 10;
myClassObj1.myString = "Obj1: Hello World";
myClassObj2.myData = 20;
myClassObj2.myString = "Obj2: Hello World";
alert( myClassObj1.myData ); // displays: 10
alert( myClassObj1.myString ); // displays: "Obj1: Hello World"
alert( myClassObj2.myData ); // displays: 20
alert( myClassObj2.myString ); // displays: "Obj2: Hello World"
MyClass is still incomplete because there are no behaviors assigned to it. To add methods to MyClass, properties that reference functions are added to MyClass:
{
this.myData = 5;
this.myString = "Hello World";
this.ShowData = DisplayData;
this.ShowString = DisplayString;
}
function DisplayData()
{
alert( this.myData );
}
function DisplayString()
{
alert( this.myString );
}
var myClassObj1 = new MyClass();
var myClassObj2 = new MyClass();
myClassObj1.myData = 10;
myClassObj1.myString = "Obj1: Hello World";
myClassObj2.myData = 20;
myClassObj2.myString = "Obj2: Hello World";
myClassObj1.ShowData(); // displays: 10
myClassObj1.ShowString(); // displays: "Obj1: Hello World"
myClassObj2.ShowData(); // displays: 20
myClassObj2.ShowString(); // displays: "Obj2: Hello World"
The figure above defines a complete abstract data types or class in JavaScript. MyClass now defines a concrete type possessing data and associated behaviors.
Encapsulation
Using MyClass as defined above permits accessibility of its internal data representation as well as having its methods and variable names global in scope increasing the risk of name collisions. Encapsulation supports data hiding and the concept of viewing objects as self-contained entities providing services to consumers.
{
var m_data = 5;
var m_text = "Hello World";
this.SetData = SetData;
this.SetText = SetText;
this.ShowData = DisplayData;
this.ShowText = DisplayText;
function DisplayData()
{
alert( m_data );
}
function DisplayText()
{
alert( m_text );
return;
}
function SetData( myVal )
{
m_data = myVal;
}
function SetText( myText )
{
m_text = myText;
}
}
var myClassObj1 = new MyClass();
var myClassObj2 = new MyClass();
myClassObj1.SetData( 10 );
myClassObj1.SetText( "Obj1: Hello World" );
myClassObj2.SetData( 20 );
myClassObj2.SetText( "Obj2: Hello World" );
myClassObj1.ShowData(); // displays: 10
myClassObj1.ShowText(); // displays: "Obj1: Hello World"
myClassObj2.ShowData(); // displays: 20
myClassObj2.ShowText(); // displays: "Obj2: Hello World"
JavaScript treats a class definition as a function definition and uses the var keyword to define local variables. Therefore var indicates that m_data is a local or "private" variable of MyClass. In figure 6, var was not used and thus, MyData is global in scope or "public". The var keyword is how encapsulation is specified in JavaScript.
Inheritance
JavaScript supports inheritance through the use of object prototypes. A prototype is a template of properties that is shared by all instances of the object type. Thus instances of object types "inherit" the values of its prototype property. In JavaScript, all object types have a prototype property that can be both extended and inherited.
In the example below, the Shape prototype object defines three properties, GetArea, GetPerimeter, and Draw, that reference the functions, Shape_GetArea, Shape_GetParameter, and Shape_Draw. Every instance of Shape inherits the prototype allowing them to call the Shape functions through the properties.
To achieve inheritance in JavaScript, the prototype of a previously defined object type is copied into the prototype of the derived object type. The Shape prototype below is copied into prototype of the derived Circle and Rectangle object types. Then the Draw property is overridden supplying the proper the function reference for the new class.
Shape.prototype.GetParameter = Shape_GetParameter;
Shape.prototype.Draw = Shape_Draw;
function Shape()
{
}
function Shape_GetArea()
{
return this.area;
}
function Shape_GetParameter()
{
return this.parameter;
}
function Shape_Draw()
{
alert( "Drawing generic shape" );
}
Circle.prototype = new Shape();
Circle.prototype.constructor = Circle;
Circle.prototype.baseClass = Shape.prototype.constructor;
Circle.prototype.Draw = Circle_Draw;
function Circle( r )
{
this.area = Math.PI * r * r;
this.parameter = 2 * Math.PI * r;
}
function Circle_Draw()
{
alert( "Drawing circle" );
}
Rectangle.prototype = new Shape();
Rectangle.prototype.constructor = Rectangle;
function Rectangle( x, y )
{
this.area = x * y;
this.parameter = 2 * x + 2 * y;
}
Rectangle.prototype = new Shape();
Rectangle.prototype.constructor = Rectangle;
Rectangle.prototype.baseClass = Shape.prototype.constructor;
Rectangle.prototype.Draw = Rectangle_Draw;
function Rectangle( x, y )
{
this.area = x * y;
this.parameter = 2 * x + 2 * y;
}
function Rectangle_Draw()
{
alert( "Drawing rectangle" );
}
var circle = new Circle( 10 );
var rectangle = new Rectangle( 10, 20 );
alert( "Circle base class = " + circle.baseClass );
alert( "Circle area = " + circle.GetArea() );
alert( "Circle parameter = " + circle.GetParameter() );
circle.Draw();
alert( "Rectangle base class = " + rectangle.baseClass );
alert( "Rectangle area = " + rectangle.GetArea() );
alert( " Rectangle parameter = " + rectangle.GetParameter() );
rectangle.Draw();
Circle and Rectangle prototypes are assigned to the prototype of the Shape class through an object instance thereby "inheriting" the methods assigned to the prototype array of the Shape class. An instance of the Shape class is necessary to create a copy of the Shape prototype array. This permits overriding of the Shape methods yet preserving the original prototype array associated to all Shape objects.
Both the Circle and Shape classes extend the Shape class by overriding Draw method supplying its own respective implementation while inheriting the Shape implementation of the GetArea and GetParameter methods:
var rectandle = new Rectangle( 10, 20 );
alert( "Circle base class = " + circle.baseClass );
// Alert: Circle base class = <STRING constructor Shape of output>
alert( "Circle area = " + circle.GetArea() );
// Alert: Circle area = 314.1592653589793
alert( "Circle parameter = " + circle.GetParameter() );
// Alert: Circle parameter = 62.83185307179586
circle.Draw();
// Alert: Drawing circle
alert( "Rectangle base class = " + rectangle.baseClass );
// Alert: Rectangle base class = <STRING constructor Shape of output>
alert( "Rectangle area = " + rectangle.GetArea() );
// Alert: Rectangle area = 200
alert( "Rectangle parameter = " + rectangle.GetParameter() );
// Alert: Rectangle parameter = 60
rectangle.Draw();
// Alert: Drawing rectangle
Polymorphism
Polymorphism defines disparate behaviors and actions by objects to the same function invocation. The Draw property of Shape, Circle, and Rectangle object types is polymorphic. The Draw property invokes a different function depending upon its object type.
var circle = new Circle( 10 );
var rectandle = new Rectangle( 10, 20 );
shape.Draw();
// Alert: Drawing generic shape
circle.Draw();
// Alert: Drawing circle
rectangle.Draw();
// Alert: Drawing rectangle
Demo
The demo program, JsOOPDemo, is a standard ASP.NET application running only JavaScript with a blank web page as its UI. The URL for this site is http://localhost/JsOOP/JsOOPDemo/WebForm1.aspx. This means you should copy the source code into the JsOOP/JsOOPDemo subdirectory of your http://localhost home directory. You can use Visual Studio to create the ASP.NET project or use the IIS management tool found in Administrative Tools of the Control Panel. This demo has been tested on both version 1.0 and 1.1 of the runtime.
The demo contains examples of the JavaScript found in the article and running the demo will help you explore writing object-oriented code in JavaScript.
Conclusion
JavaScript prototypes and function objects provide object-oriented features customary to object-oriented programming language. What is confusing is that JavaScript does not intrinsically support class inheritance and clumsily supports it through prototype inheritance. Part 2 of this series introduces a framework that simplifies JavaScript class inheritance and demonstrates writing a class hierarchy of JavaScript object types.
Kaynak www.codeproject.com/aspnet/JsOOP1.asp