I’d like to solicit your opinion on something.
As you probably know, JavaScript is a very flexible language. There’s always more than one way of doing something. That becomes really apparent when it comes to objects.
Here’s one way of creating an object called Foobar
. This object has some properties, foo
and bar
, and methods, init
and execute
:
function Foobar() {
var foo,bar;
this.init = function() {
foo = this.foo;
bar = this.bar;
execute();
};
var execute = function() {
alert(foo+bar);
};
}
Here’s how you would create a new instance, assign values to the properties, and invoke the init
method:
var baz = new Foobar();
baz.foo = 1;
baz.bar = 2;
baz.init();
The execute
method is effectively private and can only be called from within the object, but the properties can be set externally.
Here’s another way of doing much the same thing:
function Foobar() {
var foo,bar;
this.setFoo = function(value) {
foo = value;
};
this.setBar = function(value) {
bar = value;
};
this.init = function() {
execute();
};
var execute = function() {
alert(foo+bar);
};
}
This time, the properties are given values only through methods like setBar
and setFoo
:
var baz = new Foobar();
baz.setFoo(1);
baz.setBar(2);
baz.init();
The end result is pretty much the same. You can create an instance and set some properties (foo
and bar
), but the only method you can invoke is init
: the execute
method remains more or less private in both cases.
So my question is: which do you prefer?
The second one looks more like a classic example of a class that you’d see in other programming languages, but the first one is slightly lighter.
I’m aware that there are other ways of doing the same thing. I haven’t even touched on using the object literal, which would save even more space, I think.
Right now, I’m just interested in a quick show of hands. Which one do you prefer, and why? Which one do you find more readable and easy to understand, the first or second?
Posted by Jeremy on Wednesday, October 25th, 2006 at 3:50pm
Comments
I think it’s good to have a layer of abstraction, so you can build logic into your property get / set if necessary. If a consumer is directly accessing the member variables, you have no level of control about what gets returned. In a more full-blown programming language these members would typically be private, allowing you to encaspulate your implementation and keep it seperate from the public interface to your class.
Taking this approach allows you to expose properties that might not correspond to directly returning the value of a single member variable - for example, you could expose a ‘FullName’ property that returns a value based on title, first name, and surname.
So I’d go for option 2, but I can understand wanting to keep it simple…
Neither. I prefer to use prototypes because every time I don’t I find myself (or rather collegues editing my code) blowing the lid off of IE6’s memory management. An hour after working with a complicated js site and IE is using 1gb of RAM.
A while back I wrote a function to implement class-based inheritance (similiar to Dean Edwards’ Base Class) and told everyone to use that and reference EVERYTHING using "this." It doesn’t exactly prevent memory leaks, but it certainly helps to prevent them, and I have one less path to check when I’m trying to find them.
for obvious reasons, foobar.prototype.setbar is going to (potentially) take up much less memory in the long run than foobar.setbar. It would be nice to find a way to implement private variables using prototypes but unfortunately one doesn’t exist (yet).
# Posted by Mark Kahn on Wednesday, October 25th, 2006 at 5:32pm
Coming from a Cplusplus /Java/Perl background, I’d say follow this rule of thumb:
If you have restrictions on values that your member variables can have, then use a setter/getter. If you don’t, avoid them. Also, when using setters/getters, I find that it’s better to have a short meaningful name for your setter/getter than to reserve that for your private variable and give the setter/getter a more verbose name.
Therefore, I’d do something like:
var _foo, _bar;
this.foo = function(value) { if(arguments.length == 1) _foo = value; return _foo; }
That way, you can call it like:
obj.foo(3);
alert(obj.foo());
PS: Excuse the formatting, I couldn’t figure out how to get pre working in the comments PPS: I also couldn’t figure out how to enter a plus sign into the comments - the preview seems to strip them off.
# Posted by Philip on Wednesday, October 25th, 2006 at 8:30pm
I use the second method quite heavily in PHP, but not so much in Javascript. I think the decision on which technique to use comes down to the importance of the variable. Were I to anticipate that the variable would be often reasigned from an outside source I would use method #2, otherwise I would stick with method #1. Additionally the number of variables would play a part. Should there be a large quantity of variables to set I would use method #1, otherwise method #2.
I’ve always found I use more shortcuts and a compact writing style with Javascript as opposed to other languages. For readability purposes I would write method #2 as
function Foobar() {
var foo,bar;
this.setFoo = function(value) { foo = value; };
this.setBar = function(value) { bar = value; };
this.init = function() {
execute();
};
var execute = function() {
alert(foo bar);
};
}
# Posted by Steve Tucker on Wednesday, October 25th, 2006 at 9:03pm
I prefer the first method because it is easier for me to read and understand, most likely because it is similar to how I first learned to write objects in JS. As you pointed out, the second one looks more like server-side code, and since I’m a front-end developer, I have no desire to write my JS code in that format. Shorter JS code works best for me.
# Posted by John Riviello on Wednesday, October 25th, 2006 at 10:15pm
The first one. JavaScript is supposed to be simple, so if you really want more formal class definitions, switch to that language. KISS when it comes to JavaScript.
# Posted by Mufasa on Wednesday, October 25th, 2006 at 10:49pm
Everybody knows that the best programmers are lazy and dumb (http://blog.outer-court.com/archive/2005-08-24-n14.html), so I’d probably opt for option #2 because it takes less effort. However, as Oli points out in the first comment, using option #1 (accessor methods) provides you with an extra layer of abstraction that allows you to either filter the data being assigned to the object member, or add extra logic - it’s also an accepted practice.
With all this in mind, my answer would be: whichever method fits the bill! ;-)
# Posted by Tim Huegdon on Wednesday, October 25th, 2006 at 10:57pm
Jeremy, the second example will create a new instance of each method every time you create a new object. So if you have:
var baz1= new Foobar();
var baz2= new Foobar();
baz1.setFoo != baz2.setFoo
This can be a big problem for memory usage.
You’re almost always better off using the prototype method (for all its warts). On the other hand, I often use nested functions to achieve much the same effect as private methods.
And don’t get me started about the ugly prototype (the library) practice of declaring classes.
# Posted by Jeff Watkins on Thursday, October 26th, 2006 at 2:00am
Given these two options, i would certain prefer the latter. In the first, there’s nothing in the interface that even mentions foo or bar, so it’s unnatural to expect the caller to know to set them. Also, as Oli mentioned right off, the abstraction provides valuable protection.
To keep it simple, I’d propose a third option:
baz.init({foo: 1, bar: 2});
# Posted by Matthias Miller on Thursday, October 26th, 2006 at 4:46am
JavaScript is JavaScript. It doesn’t need to be conforming of other programming languages’ methods so I’d go with the first one because that’s the real benefit of JavaScript: simple, easy to read, and it rocks!
# Posted by Martin on Thursday, October 26th, 2006 at 2:02pm
Definitely the latter. Encapsulation is good in any language in any setting, period. If you need to change the behavior of those variables (and you will), it’ll be a lot easier with the second method. Now if only JS had properties a la C#, you could have the best of both worlds.
# Posted by Jeff Cutsinger on Thursday, October 26th, 2006 at 4:12pm
as someone who first learned Java, then struggled to learn and read Javascript.. i prefer the latter.. but still find the fact that you need to call the overall object/class a "function", then you have more "functions" inside of that, confusing.
Java describes it cleanly.. i.e. a a Class encapsulates attributes and methods.. and the word "Class" isn’t re-used again for describing methods. A Class is a class and a method is a method. Of course, there are inner Classes as well.. but they act like a Class. There is no ambiguity.
Flexibility is handy of course. I love Perl as well, where there are many way to do the same thing.. but calling all these things a "function" in javascript is confusing and makes it challenging to debug things for those of us who learned an objected oriented language before javascript.
P.S. love the Dom Scripting book.. Great piece of work.
Reading your post made me think you wrote a plugin for Foobar2000
Generally, I prefer the second because it allows you to do check the value before you set it, so it prevents it from being set to an illegal value. Although, in your short examples, any value is valid effectively valid, so the first would be fine.
But say, for example, your object depended upon foo and bar being integers within a certain range, you’d have to do the second so that you could check.
The other alternative, which is the best of both worlds, is to do the following. Unfortunately, browser support is an issue here.
function Foobar() {
var _bar = 0;
this.__defineGetter__("bar", function() { return _bar; });
this.__defineSetter__("bar", function(value) {
if (value >= 0 && value <= 100) _bar = value;
});
}
var x = new Foo();
x.bar = 10;
x.bar = 150;
alert(x.bar);
# Posted by Lachlan Hunt on Sunday, October 29th, 2006 at 3:01pm
For the quick show of hands, I prefer the first.
If you’re not going to make any verifications, go straight ahead and set the vars straight away.. but sometimes ("it depends") you might want that little layer for verification purposes, so you can have hybrids. Some properties have getter/setters others don’t…
This would make much more sense if you could protect variables against public access, ie, specify the level of access you wanted for each property (public, private, protected… as in Java or other OO languages). Is there a way to do this I don’t know of? If there isn’t, then it actually makes no difference, since someone using that object can get/set the value without using the getters/setters.
# Posted by André Luís on Thursday, November 2nd, 2006 at 4:08am
For the show of hands, I put both of my hands up for number one. I simply disgusted by getters and setters as separate (public) functions. If you need to protect the interface to members via functions, one should be able to to declare accessors like you do in python [1] or delphi [2].
I know that this isn’t supported in javascript, but as an answer to what I like best it’s the answer.
[1] http://docs.python.org/lib/built-in-funcs.html#l2h-57
[2] http://www.delphibasics.co.uk/RTL.asp?Name=Property
# Posted by jens persson on Tuesday, November 7th, 2006 at 6:14pm
You could effectively pass in your values to the constructor - by passing the extra setters for the client code.
var F = function(a, b) { this.init(a, b); }; Foo.prototype = { setA : function(name) {
},
setB : function(name) {
},
init : function(a, b) {
this.setA(a);
this.setB(b);
}
};
/* * instantiate :) */
var o = new F(‘hello’, ‘world’);
But yeah, if I had to pick - I’d pick your second option (over the first) Jeremy :)
# Posted by Dustin Diaz on Wednesday, November 8th, 2006 at 1:23am
To this day I still have no idea how to post code on this website and make it look good. Will you be a good lad and share with us your comment input policy?
# Posted by Dustin Diaz on Wednesday, November 8th, 2006 at 1:25am
Do you know how well supported watch and unwatch are? if they are well supported, it would make the first choice much, much better.
# Posted by Jeff Cutsinger on Thursday, November 9th, 2006 at 9:56pm
Sorry, didn’t realize links weren’t allowed. The url for a reference is http://developer.mozilla.org/en/docs/CoreJavaScript1.5Reference:GlobalObjects:Object:watch
# Posted by Jeff Cutsinger on Thursday, November 9th, 2006 at 9:58pm
Sorry. Comments are closed.