Reputation: 24340
I am trying to wrap my head around the idea of classes, data visibility and closures (specifically in Javascript) and I am On the jQuery docs page for types, it mentions that closures are used to hide data:
The pattern allows you to create objects with methods that operate on data that isn't visible to the outside—the very basis of object-oriented programming.
The example:
function create() {
var counter = 0;
return {
increment: function() {
counter++;
},
print: function() {
console.log(counter);
}
}
}
var c = create();
c.increment();
c.print(); // 1
By declaring the variable counter with the keyword var, it is already locally scoped inside the function/class definition. As far as I know and can tell, it isn't accessible from the outside to begin with. Am I missing something from a data visibility perspective.
Second, is there an advantage to writing the class like above versus like below:
function create() {
var counter = 0;
this.increment = function() {
counter++;
}
this.print = function() {
console.log(counter);
}
return this;
}
var c = create();
c.increment();
c.print(); // 1
As I understand it, these are more or less semantically the same thing - the first is just more "jQuery style". I am just wondering if there is an advantage or other nuance I don't fully appreciate from the first example. If I am correct, both examples create closures in that they are accessing data declared outside their own scope.
http://docs.jquery.com/Types#Closures
Upvotes: 12
Views: 3888
Reputation: 11
MYAPP = (function(){
var v1,v2;
return {
method1:function(){},
method2:function(){}
};
})();
I always use closures like this in my application, as do this, all my own defined methods are in MYAPP namespace ,the v1 and v2 are only accessible by methods in MYAPP.In my application, I often only write a "app.js" file,all my js codes inside. I guess you can define a method called "registy" to define private variable in MYAPP, then you can use it in your methods. All extra variables and methods should be defined by registy method when you wanna add extra codes in html file, just like JQuery.extend method. I've heard if use too many closures in IE browser, you are easy to get stack overflow. (In my opinion)
Upvotes: 1
Reputation: 22911
This syntax makes more sense to me coming from an OOP background:
Create = function {
// Constructor info.
// Instance variables
this.count = 10;
}
Create.prototype = {
// Class Methods
sayHello : function() {
return "Hello!";
},
incrementAndPrint : function() {
this.count++;
// Inner method call.
this.print();
},
print : function() {
return this.count;
}
}
var c = new Create();
alert(c.incrementAndPrint());
Upvotes: 1
Reputation: 58931
Well, I don't care to get into a religious war over how to create objects in JavaScript, since some people feel strongly that there is a right and wrong way to do it.
However, I want to point out something in your second set of code that isn't too savory - namely, the fact that you are assigning things new properties on the object contained in the this
keyword - do you realize what that object is? It isn't an empty object unless you use instantiation syntax like this:
var c = new create();
When you do that, the this
keyword inside the body of the constructor function is assigned a brand new object, as though the first line in the body were something like:
this = {};
But when you call create()
as a function, as you do in that example, you are altering the scope outside of the function's definition (as alluded-to by @seanmonster in the comments).
Upvotes: 2
Reputation: 30723
You should compare the example against this snippet
function create() {
this.counter = 0;
this.increment = function() {
this.counter++;
};
this.print = function() {
console.log(counter);
}
}
var c = new create();
c.increment();
c.print(); // 1
So when new create() is called it initializes the new object with two methods and one instance variable (namely: counter). Javascript does not have encapsulation per-se so you could access c.counter, as follows:
var c = new create();
c.increment();
c.counter = 0;
c.print(); // 0
By using closures (as shown in your examples) counter is now longer an instance field but rather a local variable. On the one hand, you cannot access from outside the create() function. On the other hand, increment() and print() can access because they close over the enclosing scope. So we end up with a pretty good emulation of object-wise encapsulation.
Upvotes: 2
Reputation: 32468
In your second example, when you call create()
, within the scope of the function, this
is the global object (which is always the case when you call a "bare" function, without using it as a constructor or accessing it as a property (e.g. a "method" call)). In browsers, the global object is window
. So when you call create subsequent times, it creates new closures, but you then assign them to the same global object as before, overwriting the old functions, which is not what you want:
var c = create(); // c === window
c.increment();
c.print(); // 1
var c2 = create(); // c2 === c === window
c.print(); // 0
c2.print(); // 0
increment(); // called on the global object
c.print(); // 1
c2.print(); // 1
The solutions, as others have pointed out, is to use new create()
.
Upvotes: 0
Reputation: 125488
By declaring the variable counter with the keyword var, it is already locally scoped inside the function/class definition. As far as I know and can tell, it isn't accessible from the outside to begin with. Am I missing something from a data visibility perspective.
It's not that the counter
variable isn't accessible from outside the function to begin with, it's that it is accessible to the increment
and print
functions after create
function has exited that makes closures so useful.
Upvotes: 4
Reputation: 17544
Christian Heilmann has a fairly decent article on the module pattern that you describe that might help you wrap your head around it and why it's useful.
Upvotes: 1
Reputation: 211982
First of all, you are correct that both versions use closures.
The first version is cleaner (in my opinion) and more popular in modern javascript. The major potential drawback of the first style is that you cannot effectively assign objects to the constructor's prototype, which is useful (and more efficient) if you are creating a lot of the same objects.
The second style, I've actually never seen in production Javascript. Normally, you would instantiate create
with new
, instead of returning this
in the create()
function, like so:
function create() {
var counter = 0;
this.increment = function() {
counter++;
}
this.print = function() {
console.log(counter);
}
}
var c = new create();
c.increment();
c.print(); // 1
Upvotes: 11
Reputation: 415790
Your 2nd example does still use closures, because the increment and print functions still act on a variable otherwise is out of scope — by the time you call c.increment()
the create function has already exited.
I like the first example because it avoids the "this
" keyword, and in javascript "this
" can be tricky — it doesn't always refer to what it seems like it should.
Upvotes: 1