0

I came across this on Mozilla's tutorials on JavaScripts and I cant seem to understand it. I read a lot of stackoverflow questions but I could not find an answer to my question. Given below is a code snippet.

var createPet = function(name) {
  var sex;

  return {
    setName: function(newName) {
      name = newName;
    },

    getName: function() {
      return name;
    },

    getSex: function() {
      return sex;
    },

    setSex: function(newSex) {
      if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie");
var pet2 = createPet("Sam");
pet.getName();                  // Vivie
pet2.getName();                 // Sam

createPet only seems to return a map of function objects but there is no mention of variable name anywhere but somehow, pet and pet2 behave like objects of a class with a member variable named name and a bunch of member functions like getName(), setName() etc. How does this work?

BegaluruBoy
  • 95
  • 2
  • 7
  • `name` and `sex` are normal variables, not members of any object. Thinking they are members of an object is only going to cause more confusion. There is a huge difference between object property and a variable. – Esailija Aug 25 '12 at 07:05

2 Answers2

3

name is the argument to the createPet() function. It works identically to the local variable sex and behaves as a private member variable that is only accessible to the functions declared inside of createPet(). name is passed into createPet(), but it can also be set from within createPet() and it survives the execution of createPet() because of the closure created by the functions in the returned object which all have a reference to the name argument.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Ok that kind of makes sense except here createPet is an anonymous function. Here it appears that createPet() is behaving like a constructor of an object. So everytime you call createPet(), it creates a new name and sex variables and all associated methods. Is that correct? – BegaluruBoy Aug 25 '12 at 06:46
  • @BegaluruBoy - `createPet` isn't anonymous - it's a variable that contains a function. That function creates and returns an object that has a number of functions as properties. Because of a closure, those functions have access to both `sex` and `name` as variables within their scope that they can read/change and they exist per instance so they work like member variables, but they are private. Yes, it is behaving like a constructor. It's actually more like a factory function because you don't use it with `new`. You call it and it creates an object. – jfriend00 Aug 25 '12 at 06:53
  • Sorry you are right. Its not an anonymous function. A more high level question. This seems so counter intuitive. What's the reasoning behind it? – BegaluruBoy Aug 25 '12 at 07:05
  • Factory functions are used in lots of places and is a common design pattern. Look at `document.createElement()`. It's a factory function. You call it and it returns a new object. Look at `$(selector)` in jQuery - you call it and it returns a new object. – jfriend00 Aug 25 '12 at 07:14
  • @jfriend00 those use the real object model, not anything like this. – Esailija Aug 25 '12 at 07:15
  • @Esailija - `jQuery(selector)` is just a function that returns a javascript object. No different than the example in the OP's question. I mentioned `document.createElement()` because it's also a factory function. Yes, it creates a DOM element rather than a pure javascript object, but the concept is the same. Javascript could have made everything work like `var img = new Image();`, but they chose to use a factory function instead. Factory functions are a common design pattern. Factory functions in javascript can use private variables in a closure if they so desire, but it is not required. – jfriend00 Aug 25 '12 at 07:18
  • @jfriend00 I mean I think he was referring to this whole thing, which is indeed very counter-intuitive. Especially so when people throw around completely false terms like "private member". In many languages, you can access the object properties without `this.`, but when you do so here, you are not doing that but you are accessing normal variables technically unrelated to the object. So to someone, it might look like it's the same as implied property access but that's not what's happening. – Esailija Aug 25 '12 at 07:33
  • @Esailija - Yes, a variable in a closure like this is a bit odd until you learn it, really understand how it works and fully understand how to take advantage of it. Once you do, it just seems like a feature of the language that can be used quite handily in lots of circumstances. – jfriend00 Aug 25 '12 at 07:45
0

The name is defined as a formal parameter like function( name /*<-- there it is */).

So think of:

= function( name ) {
    var sex;
};

As:

= function() {
    var name = arguments[0];
    var sex;
}

They are indeed exactly, or at least effectively, the same.

And yes, createPet returns a dumb map of promiscuous closures. In fact, you could do this and see it still work:

var a = createPet("Vivie");

var asd = a.setName;

asd("asd");

a.getName() //"asd"

This is because the object is pretty much irrelevant, it's the functions that carry the data not the object. The object is just a dumb map of functions (closures).

You will also not deal with object properties but variables. And you cannot pretend variables are object properties unless it's a global variable which is not a variable actually, but a property of the global object:

  • Properties can be enumerated, dynamically accessed, deleted and made constant
  • Properties can be accessed anywhere, as long as you have a reference to the object

On top of this, the whole model is backwards. You have behavior that carries data instead of data that carries behavior.

You don't have to do this at all of course, see https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Details_of_the_Object_Model.

Esailija
  • 138,174
  • 23
  • 272
  • 326