So many web developers know just a little about Javascript. Enough to use a library like jQuery somewhat effectively, but not enough to understand how to create properly structured applications. I'm not going to get into how objects work in Javascript, or how to use them, I'll leave that for another day. I just want to touch on the topic of member visibility (that is, private/protected methods and properties).
There is no formal way to actually define member visibility in Javascript (no "public" and "private" keywords like you might see in Java). Instead we just use the language features in such a way that it creates the same public/private situation as we'd expect in other languages (or nearly the same).
Public
A member is public if "the world" can access it. For methods this means anyone can call on it, and for properties this means anyone can use its value or change it. If you are creating object-oriented programs, having nothing but public members breaks a lot of the ideology. In many languages, like PHP before version 5, programmers used conventions to denote members were private and not to be used. Sometimes this works, but true encapsulation is much better.
There are two ways you create public members:
Constructor
-
function Person() {
-
this.name = 'Christopher';
-
}
-
-
var me = new Person();
-
alert(me.name);
Using 'this' binds the variable to the object, so anyone can use it.
Prototype
-
function Person() {
-
-
}
-
-
Person.prototype.name = 'Christopher';
-
-
var me = new Person();
-
alert(me.name);
Adding a method or property to the prototype also makes it public.
Private
A member is private if only the object itself can access it.
To create private members, you define them within the constructor:
-
function Person() {
-
var first_name = 'Christopher';
-
var last_name = 'Nadeau';
-
-
function fullname() {
-
return first_name + ' ' + last_name;
-
}
-
}
-
-
var me = new Person();
-
alert(me.name); // can't do it!
In this example there are two private properties for first and last name, and one private method to return a full name.
Some Issues...
There are a couple of issues you have to be aware of when using private members.
Only methods defined in the constructor can use private members. That is to say, we cannot do something like this:
-
function Person() {
-
var first_name = 'Christopher';
-
var last_name = 'Nadeau';
-
-
function fullname() {
-
return first_name + ' ' + last_name;
-
}
-
}
-
-
Person.prototype.alertName = function() {
-
// Can't do this. 'this.first_name' does not actually exist!
-
alert(this.first_name);
-
}
The scope of the private members are in the constructor only. To get around this problem, you must use what is called privileged methods.
Privileged
As stated before, only methods defined within the constructor have access to private members. Given that many objects are built around using 'prototype', this is problematic. The solution is to create privileged methods.
A privileged method is a method defined in the constructor but assigned to a public variable. Since it is defined within the scope of any other private members, it has access to them. Here's an example:
-
function Person() {
-
var first_name = 'Christopher';
-
var last_name = 'Nadeau';
-
-
function fullname() {
-
return first_name + ' ' + last_name;
-
}
-
-
this.alertName = function() {
-
alert(first_name);
-
}
-
}
You can guess that you will be able to retrieve the 'alertName' method or reset it. You might think that it would be possible to redefine the method to take control of some other private member. Not so! The method is a closure and retains the scope in which it was defined. Since it was defined in the scope of the other private members, it has access to them. There is no way to recreate this scope, so even reassigning a completely new function will not allow you to expose any private members.
The 'this' reference
Another thing to be aware of is that functions defined within the constructor have "this" pointing to themselves, and not the object itself. Thus if you need to call other public methods, you cannot. For example, let's make the fullname() method privileged and modify alertName() to call it:
-
function Person() {
-
var first_name = 'Christopher';
-
var last_name = 'Nadeau';
-
-
// This is now a privileged method
-
this.fullname = function() {
-
return first_name + ' ' + last_name;
-
}
-
-
this.alertName = function() {
-
alert(this.fullname()); // wont work
-
}
-
}
To get around this problem, Javascript programmers assign another private property with the value of 'this' inside the constructor, and use that variable as a reference to the object. The two most common names are 'self' and 'that'; I prefer to use 'self'. Here is the final object, working as expected:
-
function Person() {
-
var first_name = 'Christopher';
-
var last_name = 'Nadeau';
-
-
// Create a reference to this object so closures
-
// can access it.
-
var self = this;
-
-
this.fullname = function() {
-
return first_name + ' ' + last_name;
-
}
-
-
this.alertName = function() {
-
alert(self.fullname()); // Using self to reference the object
-
}
-
}
May 26th, 2008 at 6:58 pm
Very concise explanation, especially the later part regarding the nuances with prototyped functions, private variables, Privileged functions and the 'this' reference.
Thanks for your time spent writing this,
Regards,
Steve
June 10th, 2010 at 12:26 pm
All extremely true, but I don’t take that view myself. I will stick the more regular view.