Prototypes in JavaScript
In the JavaScript the same thing you can do in many different ways.
Consider the examples:
1:
function Circle(radius) {
return {
"r" : radius,
"area" : function(){
return Circle.pi * this.r * this.r;
}
}
}
Circle.pi = 3.14159;
var a = Circle(10);
alert(a.area());
2:
function Circle(radius) {
this.r = radius;
}
Circle.pi = 3.14159;
Circle.prototype.area = function(){
return Circle.pi * this.r * this.r;
}
var a = new Circle(10);
alert(a.area());
The second is better than first because we dont define the same function area for any instance of the Circle.
But lets consider 3:
function Circle(radius) {
return {
"r" : radius,
"area" : Circle.area
}
}
Circle.pi = 3.14159;
Circle.area = function(){
return Circle.pi * this.r * this.r;
}
var a = Circle(10);
alert(a.area());
Is there any reason to prefer second开发者_运维百科 style instead of third? Or I misunderstood something at all?
I would definitely go with example 2. Neither example 1 or 3 make good use of JavaScript's object-oriented features because:
- You duplicate method definitions in each instance.
- By returning a new object instead of using
this
, you lose the identity of the class, i.e. you can no longer do checks likea instanceof Circle
. - You give up the possibility of inheritance since you do not use prototypes.
Is there any reason to prefer second style instead of third?
The third style is still wasting a small amount of space in order to store the association between the name area
and the area function.
Also because you are returning a new Object
from the {...}
literal, instanceof Circle
won't work.
The second is better than first because we dont define the same function area for any instance of the Circle.
Indeed, but it can sometimes be useful to define a new copy of each method—a copy that, thanks to closures, can know what object it is bound to. With traditional prototyping you only get the this
context which is set from the object the caller retrieved it from, rather than bound to a particular object. The first way avoids the problems of this
-retention in things like event handlers, at the price of some loss of efficiency.
The difference between Circle.prototype.area
and Circle.area
is that one is only accessible with a instance of the class.
Circle.prototype.area = function(){};
Circle.area // wrong
(new Circle()).area // ok (you need a Circle object)
and
Circle.area = function(){};
Circle.area // ok (you don't need the object, its static)
(new Circle()).area // wrong
The problem with example 2 is, that when you accidentally forget to use the new
operator when creating the object, and just call var a = Circle(10);
(which is fine for the 3rd example, but not for the 2nd), then your constructor creates big trouble:
this.r = radius;
Since you didn't use new
, this
will be assigned the global object, so the constructor really sets the global variable r
!
So I would strongly prefer example 3.
I'd like to comment on "The second is better than first because we dont define the same function area for any instance of the Circle." In practice, this may be such a negligable advantage as to be totally insignificant. I tend to favor syntax and human-friendly code over efficiency, unless performance is noticably affected. I've been completely ignoring the Prototype feature of javascript and also avoiding the use of "this", basically using technique #1. It's working just fine for me. I find avoiding "this" prevents a lot of confusion about just what "this" refers to. It's perfectly possible to create inheritance-type structures without "prototype". You simple create a superclass object, and override whichever methods you want. Douglas Crockford has written pretty extensively on numerous different ways to do inheritance in javascript.
I will agree with casablanca above that being able to test for type using "instanceof" is a nice advantage of returning "this". Perhaps I'll take another look at "protype" and "this" on a future project. For now, though, I'm getting along just fine without them.
精彩评论