一篇關於javascript面向物件開發的論文
from:
http://mckoss.com/jscript/object.htm
Object Oriented Programming in JavaScript
by Mike KossJanuary 14, 2006 <script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"> </script> <script src="http://pagead2.googlesyndication.com/pagead/expansion_embed.js"></script>
Introduction
The first version of this paper , written in 2003, had several shortcomings, not the least of which was that the techniques described were specific to Internet Explorer. I've updated and improved on the original, to document the current state of the art, especially in light of the extensive interest in AJAX technology and the increasing adoption of the FireFox browser. All the examples presented here will follow the ECMA language standards and can be applied to Internet Explorer, FireFox, and ActionScript (in Macromedia Flash).
While early adopters of JavaScript used it as a simple scripting engine to create dynamic web pages, modern web designers have come to use more sophisticated object oriented techniques in building their code. I will present here, both the common paradigms used in object oriented JavaScript programming, and also suggest some helper functions that you can use in your code to streamline the process.
It should be noted that the current design of the JavaScript language, did not fully anticipate or fully implement an object oriented system. That is why the subject is somewhat mysterious and there are various implementations of object oriented programming techniques being used on the web today. I will describe what I believe to be the most main-stream and compatible implementation that fits most naturally into the design of the language.
Object Oriented Programming Goals
I assume that the reader has a basic familiarity with JavaScript, function calls, and the basic tenets of object oriented programming. I consider the three primary goals of object oriented programming to be:
- Encapsulation - Support for method calls on a JavaScript object as a member of a Class.
- Polymorphism - The ability for two classes to respond to the same (collection of) methods.
- Inheritance - The ability to define the behavior of one object in terms of another by sub-classing .
Through a series of examples (which, for the curious reader, are actually snippets of live JavaScript code embedded within this page), I will demonstrate how objects can be used in JavaScript and how these object oriented paradigms can be best implemented. I will cover techniques for:
- Defining a Class
- Defining and calling Methods in a Class
- Defining a Sub-Class
- Calling the Super-Class constructor from a Sub-Class
- Overriding Methods of a Super-Class in a Sub-Class
- Calling a Super-Class method from a Sub-Class
Simple Objects
Doug Crockford's book explains of the best parts of the JavaScript language.
-- Mike Koss
The simplest object oriented construct in JavaScript is the built-in Object data type. In JavaScript, objects are implemented as a collection of named properties. Being an interpreted language, JavaScript allows for the creation of any number of properties in an object at any time (unlike C++, properties can be added to an object at any time; they do not have to be pre-defined in an object declaration or constructor).
So, for example, we can create a new object and add several ad-hoc properties to it with the following code:
obj = new Object;
obj.x = 1;
obj.y = 2;
Which creates a JavaScript object which I will represent graphically like this:
obj x 1 y 2 Object.prototype constructor Object
The left hand column displays the property name of each available property on the object, while the right hand column displays it's value. Note that in addition to the x and y properties that we created, our object has an additional property called constructor that points (in this case) to an internal JavaScript function. I will explain prototype properties, below.
Defining a Class - Object Constructors
A new JavaScript class is defined by creating a simple function. When a function is called with the new operator, the function serves as the constructor for that class. Internally, JavaScript creates an Object , and then calls the constructor function. Inside the constructor, the variable this is initialized to point to the just created Object. This code snippet defines a new class, Foo , and then creates a single object of that class.
function Foo()
{
this.x = 1;
this.y = 2;
}
obj = new Foo;
obj x 1 y 2 Foo.prototype constructor Foo Object.prototype (constructor) Object
Note that we can now create as many Foo type objects as we want, all of whom will be properly initialized to have their x and y properties set to 1 and 2, respectively.
Prototypes Explained
In JavaScript, each Object can inherit properties from another object, called it's prototype . When evaluating an expression to retrieve a property, JavaScript first looks to see if the property is defined directly in the object. If it is not, it then looks at the object's prototype to see if the property is defined there. This continues up the prototype chain until reaching the root prototype. Each object is associated with a prototype which comes from the constructor function from which it is created.
For example, if we want to create an object, X, from constructor function B, whose prototype chain is: B.prototype, A.prototype, Object.prototype:
We would use the following code:
Object.prototype.inObj = 1;
function A()
{
this.inA = 2;
}
A.prototype.inAProto = 3;
B.prototype = new A; // Hook up A into B's prototype chain
B.prototype.constructor = B;
function B()
{
this.inB = 4;
}
B.prototype.inBProto = 5;
x = new B;
document.write(x.inObj + ', ' + x.inA + ', ' + x.inAProto + ', ' + x.inB + ', ' + x.inBProto);
1, 2, 3, 4, 5
x inB 4 B.prototype constructor B inA 2 inBProto 5 A.prototype (constructor) A inAProto 3 Object.prototype (constructor) Object inObj 1
In FireFox and in ActionScript, an object's prototype can be explicitly referenced via the non-standard __proto__ property. But in standard JavaScript a prototype object can only by directly referenced through the object's constructor function object.
Defining and Calling Methods in a Class
JavaScript allows you to assign any function to a property of an object. When you call that function using obj.Function() syntax, it will execute the function with this defined as a reference to the object (just as it was in the constructor).
The standard paradigm for defining methods is to assign functions to a constructor's prototype. That way, all objects created with the constructor automatically inherit the function references via the prototype chain.
function Foo()
{
this.x = 1;
}
Foo.prototype.AddX = function(y) // Define Method
{
this.x += y;
}
obj = new Foo;
obj.AddX(5); // Call Method
obj x 6 Foo.prototype constructor Foo AddX Object.prototype (constructor) Object
Polymorphism is achieved by simply having different object classes implement a collection of methods that use the same names. Then, a caller, need just use the correctly named function property to invoke the appropriate function for each object type.
function A()
{
this.x = 1;
}
A.prototype.DoIt = function() // Define Method
{
this.x += 1;
}
function B()
{
this.x = 1;
}
B.prototype.DoIt = function() // Define Method
{
this.x += 2;
}
a = new A;
b = new B;
a.DoIt();
b.DoIt();
document.write(a.x + ', ' + b.x);
2, 3
a x 2 A.prototype constructor A DoIt Object.prototype (constructor) Object
b x 3 B.prototype constructor B DoIt Object.prototype (constructor) Object
Defining a Sub-Class
The standard paradigm, is to use the prototype chain to implement the inheritance of methods from a super class. Any methods defined on the sub-class will supersede those defined on the super-class.
function A() // Define super class
{
this.x = 1;
}
A.prototype.DoIt = function() // Define Method
{
this.x += 1;
}
B.prototype = new A; // Define sub-class
B.prototype.constructor = B;
function B()
{
A.call(this); // Call super-class constructor (if desired)
this.y = 2;
}
B.prototype.DoIt = function() // Define Method
{
A.prototype.DoIt.call(this); // Call super-class method (if desired)
this.y += 1;
}
b = new B;
document.write((b instanceof A) + ', ' + (b instanceof B) + '<BR/>');
b.DoIt();
document.write(b.x + ', ' + b.y);
true, true
2, 3
b x 2 y 3 B.prototype constructor B (x) 1 DoIt A.prototype (constructor) A (DoIt) Object.prototype (constructor) Object
Something to keep in mind is that each time a sub-class is defined, we explicitly call the constructor of the super-class in order to insert it into our prototype chain. So it is important to ensure that no undesirable side-effects will occur when this call is made. Conversely, if the super-class constructor should be called for each instance of every sub-class, code must be explicitly added to the sub-class's constructor to make this call (as is done in the above example).
An Alternate Sub-Classing Paradigm
As an alternate to using the prototype chain, I've developed a method which avoids calling the constructor of a super class when each sub-class is defined. Three methods are added to the Function object:
Function.prototype.DeriveFrom = function (fnSuper) {
var prop;
if (this == fnSuper) {
alert("Error - cannot derive from self");
return;
}
for (prop in fnSuper.prototype) {
if (typeof fnSuper.prototype[prop] == "function" &&
!this.prototype[prop]) {
this.prototype[prop] = fnSuper.prototype[prop];
}
}
this.prototype[fnSuper.StName()] = fnSuper;
}Function.prototype.StName = function () {
var st;
st = this.toString();
st = st.substring(st.indexOf(" ") + 1, st.indexOf("("));
if (st.charAt(0) == "(") {
st = "function ...";
}
return st;
}Function.prototype.Override = function (fnSuper, stMethod) {
this.prototype[fnSuper.StName() + "_" + stMethod] = fnSuper.prototype[stMethod];
}
Repeating the sub-classing example using this new paradigm:
function A() // Define super class
{
this.x = 1;
}
A.prototype.DoIt = function() // Define Method
{
this.x += 1;
}
B.DeriveFrom(A); // Define sub-class
function B()
{
this.A(); // Call super-class constructor (if desired)
this.y = 2;
}
B.Override(A, 'DoIt');
B.prototype.DoIt = function() // Define Method
{
this.A_DoIt(); // Call super-class method (if desired)
this.y += 1;
}
b = new B;
document.write((b instanceof A) + ', ' + (b instanceof B) + '<BR/>');
b.DoIt();
document.write(b.x + ', ' + b.y);
false, true
2, 3
b x 2 y 3 B.prototype constructor B DoIt A A A_DoIt Object.prototype (constructor) Object
Unfortunately, this technique does not allow for the use of the instanceof operator to test for membership of a super-class. But, we have the added benefit that we can derive from more than one super class (multiple inheritance).
Private Members
Amazingly, JavaScript also can support private members in an object. When the constructor is called, variables declared in the function scope of the constructor will actually persist beyond the lifetime of the construction function itself. To access these variables, you need only create local functions within the scope of the constructor. They may reference local variables in the constructor.
function A()
{
var x = 7;
this.GetX = function() { return x;}
this.SetX = function(xT) { x = xT; }
}
obj = new A;
obj2 = new A;
document.write(obj.GetX() + ' ' + obj2.GetX());
obj.SetX(14);
document.write(' ' + obj.GetX() + ' ' + obj2.GetX());
7 7 14 7
obj GetX SetX A.prototype constructor A Object.prototype (constructor) Object
obj2 GetX SetX A.prototype constructor A Object.prototype (constructor) Object
I believe, however, that each instance of an object created in this way, has it's own copy of each local function. The local copy of the function can maintain a copy of the local scope (a closure ) of the constructor. This would be rather inefficient for object classes that construct many instances. Experiments with a single (shared) reference to a function reveal that they can only reference variables from a single instance of the class. Since the benefits of using private members is rather limited in the context of JavaScript (which is already lacking any form of type safety), I would not recommend making extensive use of the private member paradigm.
Update (June 2009): I've developed a simple library for building JavaScript Namespaces (public domain source code) - for a description see my blog .
Add New Comment
- Logged in as
- using Facebook Connect (Logout)
-V
I'm quite familiar with the class paradigm in prototype but from what I've seen there's not much about classes in the sense of OOD for jQuery. Care to elaborate or point me in a good piece about the jQuery class paradigm?
x = new B;
document.write(x.constructor == B)
it will return false instead of true. It's pretty minor, but if there is some code that is doing some inspection, or want's to clone the object by calling it's constructor, it will have a reference the superclasses constructore instead of its own.
this was very helpful...
thanx mckoss
Thanks a lot !
thanks
Nice site by the way...
Edit: deleted, There was a stupid mistakes on my proposal on entering protected behavior ... sorry
Good article!
Two questions.
1) In the section starting "Polymorphism is achieved by simply ..." you have this:
a = new A;
b = new B;
Shouldn't it be this:
a = new A();
b = new B();
2) Is there any difference between these two techniques?
function A() {
this.x = 1;
}
A.prototype.DoIt = function() {
this.x += 1;
}
and
function A() {
this.x = 1;
this.DoIt = DoIt;
}
function DoIt() {
this.x += 1;
}
2. Yes, there is a difference between assigning a function in the constructor or using a prototype. The former is more efficient to create objects - you don't allocate additional storage for the function references inside of each instance - they are all just created once in the prototype, and then referred to as needed. Functionally, both will work nearly the same; though there are differences in how functions would be "inherited" if you want to subclass this constructor.
aAAhmm... May I know other helpful site that contains information like this?....Anyways I want to ask about the attributes of an object?? can you give me an example????
This site is good!!!
salu2 y que la chela sea
B.prototype = new A;
instead of
X=new A; // Once
B.protoype = X
Ie. is it possible to share Xs?
And what does a constructor really do anyway? Is it just assigning function values to slots or is it messing with a prototype.
Is there a function in javascript that can be used to inspect object. for example in ruby, we can do object.inspect to see internal details of object
Most of the time I can use JSON, but sometimes it fails.
Thanks
function A(element)
{
this.element = element;
this.message = 'Hello';
var me = this;
this.element.onclick = function(ev)
{
alert(me.message);
}
}
I was always worried that the "me" was being created in the global scope, and there might be conflicts when more than one class declared "me" in this way, but your page has given me comfort about that...
Is there any way to copy local (private) variables to new class in inheritance?
I would like to complement you on your excellent Javascript OOP presentation.
Now three years retired, I enjoy hearing about what's happening in the programming
world -- and to read your page. VERY well done, indeed!
And may I just add, as a retiree who pays probably too much attention to the
current political scene: it's most unfortunate that something like an OO world
view is not available to politicians. It is truly bizarre that we live in a world
in which such estimable things like OOP AND 18th century political practices are
BOTH present. It's a funny old world, isn't it.
I have a question, I don't know whether it suit here, but here goes.
var pencilBox = pencilBox || {};
pencilBox = {
sideA: {},
sideB: {},
}
pencilBox.sideA = {
pencil: function(){
this.getSharp = sharpness;
}
}
pencilBox.sideB = {
sharpener: function(){
this.sharpen = sharpen;
}
}
Why this piece of code got error in IE but not in other browsers? and how should I do appropriately in JavaScript or OO manner.
p/s: omit the getSharp() and sharpen method, it’s just for beautify. :p
Thanks.
http://blog.pageforest.com/2008/07/back-from-pa...
If you're trying to create a "class" for pencilBox, you should define the methods in the pencilBox.prototype (see above). That way, all instances will
have the methods available to them.
Thanks
But the underlying concept of polymorphism more basically, getting two different "types" of objects to behave the same in some respect. In dynamic languages, like Javascript (and Python), this is acheived via "Duck Typing" (if it walks like a duck, quacks like a duck, .... it's a duck). The "contract" is simply a collection of methods that each of the objects supports that are sufficient for some piece of code to interact with them. Note that there need not be a formal base class or abstract interface defined for this to occur. Simply by implmenting methods, A, B, and, C, my object can conform to the contract and be used.
While this is a looser system, it is also more flexible. For example, contracts can consists of subsets or even overlapping collections of methods from other contracts (one contract may us methods, A and B, while another uses B and C).
Note that you can achieve EVERYTHING you can in this dynamic form or polymorphism as you can in the strongly typed languages (with the exception of type safety imposed by the language). So I don't have any qualms about using the term "polymorphism" in the JavaScript case.
(I also note the first line of the Wikipedia article you quote says exactly what I have:
"Type polymorphism in object-oriented programming is the ability of one type, A, to appear as and be used like another type, B. In strongly typed languages, this usually means that type A somehow derives from type B, or type A implements an interface that represents type B. In weakly typed languages types are implicitly polymorphic." -- mike)
// Object.prototype.objSayHello = function(){alert('Hello,from OBJECT prototype')}; // this one works as I expected, when objSayHello()
Object.prototype ={objSayHello: function(){alert('Hello,from OBJECT prototype')}}; // NOT working !
objSayHello();
// Please email me if you know, thank you! [email protected]
inB: it's actually attached to x as a property directly (x.inB)
inBProto: it's not found on x directly, but the interpreter knows to look on B.prototype to find it; it needs to know the identity of B, which is its own constructor, and then it looks on that object's prototype object and finds it like this:
x.constructor.prototype.inBProto
inA: again not a member of x, but it's also a member of B.prototype, and will be found here:
x.constructor.prototype.inA
inAProto: this one's in A's prototype, so we should have to look here:
x.constructor.prototype.constructor.prototype.inAProto
inObj: found on
x.constructor.prototype.constructor.prototype.constructor.prototype.inObj
I typed all of those expressions in in place of the shorter versions, and they all still displayed the same result: 1, 2, 3, 4, 5. So somewhere internally, when the interpreter is looking up a property name, it probably does something like this: try finding it on the object, no dice?, try it on theObject.constructor.prototype, no dice?, keep inserting another constructor.prototype in the middle there until that newly added prototype object itself is undefined, meaning of course that the property can't possibly be attached to it, so the ultimate result is undefined. Am I finally getting this?
What I DON'T get is the code on page 33 of Javascript: The Good Parts, which is heavily related to this discussion:
Function.prototype.method = function(name,func) {
this.prototype[name] = func;
return this;
}
This is designed to let you add methods without the "ugliness" of the prototype keyword in there. So, Function now has a new method method added to its prototype. But next, Mr. Crockford immediately shows how to define an integer method of Number:
Number.method('integer', function() {
return Math[this < 0 ? 'ceiling' : 'floor'](this);
});
which he puts into use immediately with:
document.writeln((-10/3).integer());
which prints "-3".
I'm having some major mental block here. We seem to be executing a method of Number called "method", and the implication is that Number got this method from the Function.prototype's method method. Does Number "inherit" from Function? It surely inherits from Object, but from Function? Would someone please help me out here?
How to inherit a class present in the head of script into div class present in the body.....
How to inherit a class present in the head of script into div class present in the body.....