0

I'm starting Addy Osmani's amazing book on javascript design patterns but can't seem to get off the ground. Can anyone tell me what's wrong here with my approach (I'm using Raphael, just for fun.):

var myPaper = Raphael('container', '800', '600');

var myScene = function() {
  var c1 = myPaper.circle(50, 50, 40);
  var c2 = myPaper.circle(50, 150, 40);
  var c3 = myPaper.circle(50, 250, 40);

  c2.attr("fill", "red");  // yep!

  return {
      firstCircle: c1
  };
}

// at some point later i want to call the function...
myScene();

//  ...then even later I want to refer to one of the circles
//  but without creating another global variable.

myScene.firstCircle.attr("fill", "red");  // nope!
console.log(myScene.firstCircle); //  undefined!

http://jsfiddle.net/aGCv8/

Ben
  • 11,082
  • 8
  • 33
  • 47

1 Answers1

3

"What's wrong with your approach" is that this isn't the module pattern, revealing or otherwise. If you meant to use it, use it, and make that function self-invoking:

var myScene = function() {
  var c1 = myPaper.circle(50, 50, 40);
  var c2 = myPaper.circle(50, 150, 40);
  var c3 = myPaper.circle(50, 250, 40);

  c2.attr("fill", "red");  // yep!

  return {
      firstCircle: c1         // ← refer to a variable which is actually defined
  };
}();                          // ← parens go here

You don't later call myScene as a function, because it isn't a function, and that anonymous function has already been called. And look, you still get access to that circle!

console.log(myScene.firstCircle); // z {0: circle.[object SVGAnimatedString], node: circle.[object SVGAnimatedString], id: 0, matrix: cb, realPath: null, paper: j…}

Omitting the parentheses (which call the anonymous function) leads to a very different outcome, as you have found.

http://jsfiddle.net/mattball/qR4Fj/

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • Thanks a lot Matt. The thing is, I actually do want to be able to call the function later, with a public pointer. When i refactor your code to do that, I'm getting a similar problem: http://jsfiddle.net/wUbG2/ – Ben Jan 29 '13 at 18:26
  • Why do you want to be able to call it later? A module is _not_ supposed to be a function; it is a self-contained logical unit of related functionality. If you want it to be reusable and applied to different locations on a page, don't create/use a module. Create and use a **class.** – Matt Ball Jan 29 '13 at 18:30
  • Well, it's called later depending on a bunch of conditional factors. (I'm simplifying greatly for the sake of the example.) It doesn't need to be reusable, or called multiple times. Not to wander off topic here, but also: my understanding of javascript is that there are no classes. I'm really just trying to figure out why myScene.secondCircle returns undefined in that second fiddle... – Ben Jan 29 '13 at 18:40
  • `myScene.secondCircle` is undefined because `c2` is `undefined` when the object defining `renderCircles` and `secondCircles` is returned. And there are definitely classes! https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript – Matt Ball Jan 29 '13 at 18:47
  • Oh, man, I knew you were going to go there. I totally appreciate the help, but an academic debate over the nature of "classes" in computer science doesn't really help me right now :) FWIW as I'm sure you know there is a debate on terminology here: http://stackoverflow.com/questions/2752868/does-javascript-have-classes If renderCircles() has run already, doesn't that define c2? – Ben Jan 29 '13 at 18:55
  • Yes, `renderCircles()` defines `c2` but because you're not using a closure, the object's reference to `c2` is not magically updated thereafter. You can fix that by doing this: http://jsfiddle.net/mattball/zQh9V/ – Matt Ball Jan 29 '13 at 18:59