Goyuix
Goyuix

Reputation: 24340

Javascript Closure and Data Visibility

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

Answers (9)

chris
chris

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

Matt
Matt

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

Jason Bunting
Jason Bunting

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

Itay Maman
Itay Maman

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

Miles
Miles

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

Russ Cam
Russ Cam

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

Steerpike
Steerpike

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

Kenan Banks
Kenan Banks

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

Joel Coehoorn
Joel Coehoorn

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

Related Questions