Hi TJ,

Thank you so much for your explanation!  That was incredibly helpful
and in depth.

--Chris

On Sep 23, 1:20 am, "T.J. Crowder" <t...@crowdersoftware.com> wrote:
> Hi Chris,
>
> There are a couple of reasons that calling functionB from functionAA
> didn't work, primarily that you called functionAA without setting its
> context (its `this` value).  So within functionAA, `this` wasn't a
> reference to the instance.  To set its `this` value, we'd either have
> to make it a property of the instance and then call it via "dotted"
> notation, or useFunction#call (orFunction#apply):
>
>     functionAA.call(this);
>
> But there are larger issues with that code as well.  You're using
> Class.create in a way that defeats the purpose of using it (by
> replacing the prototype it creates for it with a completely different
> one).
>
> Here's a recast version of that code with minimal changes:
> * * * *
> // Definition
> var Foo = Class.create({
>
>     initialize:function(params) {
>         alert('you just created an object, good work.');
>     },
>
>     functionA:function() {
>         functionAA.call(this);
>        functionfunctionAA() {
>             this.functionB();
>         }
>     },
>
>     functionB:function() {
>         alert('functionb');
>     }});
>
> * * * *
>
> Note that rather than replacing the prototype afterward, we're passing
> Class.create an object with properties for each of the methods for the
> class.  That's how Class.create is designed to be used.
>
> Regarding that functionA -> functionAA -> functionB thing, the above
> usesFunction#call and that's fine, but for completeness there are at
> least two other ways that we can do that:
>
> A) Passing the instance as a parameter
> * * * *
>     functionA:function() {
>         functionAA(this);
>        functionfunctionAA(instance) {
>             instance.functionB();
>         }
>     },
> * * * *
>
> B) Setting a variable to refer to the instance and then using that
> variable within functionAA, since functionAA closes over the variable
> (has access to it):
> * * * *
>     functionA:function() {
>         var self = this;
>         functionAA();
>        functionfunctionAA() {
>             self.functionB();
>         }
>     },
> * * * *
>
> Which one you use is largely down to personal preference and to what
> it is you're trying to achieve with defining functionAA within
> functionA, their performance is virtually identical (the version using
> the `self` variable is very, very, very slightly faster on all
> browsers, but you'd have to be doing literally hundreds of thousands
> of calls for it to matter).
>
> So, there we are, that recast code will work (unless I've let a typo
> slip in), and you'll see that sort of thing done all over the place.
> In terms of your actual question, that's the end of the answer.
>
> ...but you clearly have an interest in this stuff, so I'm going to go
> a bit off-topic and say that although the code above is fine, it does
> have a problem in that all of the functions are anonymous.  Although
> the functions are bound to properties, and the properties have names,
> the functions *themselves* do not.  This is an issue when tools try to
> tell you where you are or where something happened -- the browser
> telling you where an error occurred, or a debugger telling you what
> the call stack looks like.
>
> Lately my preference has been to use *named* functions wrapped in a
> scopingfunction(otherwise their names would leak out into the global
> namespace, and That Would Be Bad).  If we did that, it would look like
> this:
>
> * * * *
> var Foo = Class.create((function(){
>
>    functioninitialize(params) {
>         alert('you just created an object, good work.');
>     }
>
>    functionfunctionA() {
>         functionAA.call(this);
>        functionfunctionAA() {
>             this.functionB();
>         }
>     }
>
>    functionfunctionB() {
>         alert('functionb');
>     }
>
>     return {
>         initialize: initialize
>         functionA: functionA
>         functionB: functionB
>     };
>
> })());
>
> * * * *
>
> Note how instead of creating an object literal where the functions are
> directly assigned to properties, we use an anonymous scopingfunction,
> declare our functions within it in the normal way, and then have that
> scopingfunctionreturn an object that maps properties to the named
> functions (the "initialize" property refers to the "initialize"function, 
> etc.).  We then execute the anonymousfunctionimmediately,
> and pass the object it gives us into Class.create.  Now if we're using
> a debugger and we're walking through (say) functionB, the debugger can
> tell us that we're in "functionB" and that the stack looks like this:
>     (?)
>     functionA
>     functionAA
>     functionB
>
> (where that (?) is the anonymousfunctioncalled by the 'load'
> event.)  Whereas with the earlier version using mostly anonymous
> functions, the call stack would look like this:
>     (?)
>     (?)
>     functionAA
>     (?)
>
> ...because the onlyfunctionwith an actual name in the entire stack
> is functionAA.
>
> A second advantage of this scopingfunctionis that you can now easily
> and cheaply have private methods -- just leave them out of the object
> you return at the end:
> * * * *
> var Foo = Class.create((function(){
>
>    functioninitialize(params) {
>         alert('you just created an object, good work.');
>     }
>
>    functionfunctionA() {
>         functionAA.call(this);
>        functionfunctionAA() {
>             this.functionB();
>         }
>     }
>
>    functionfunctionB() {
>         alert('functionb');
>         trulyPrivate();
>     }
>
>    functiontrulyPrivate() {
>         alert("I'm truly private");
>     }
>
>     return {
>         initialize: initialize
>         functionA: functionA
>         functionB: functionB
>     };
>
> })());
>
> * * * *
>
> Nothing but the functions in your class can see the `trulyPrivate`function.  
> The way I called it in the above makes it a class method
> (rather than an instance method -- `this` has no special meaning
> within it), but there are ways to use it as an instance method if you
> like (Function#call, for instance).
>
> kangax (you'll see him in this group) has a good article on 
> namedfunctionexpressions[1] and some gotchas around browser
> implementations of them (the code above is fine for all browsers, but
> it's good to know where the gremlins are -- you couldn't safely give
> your scopingfunctiona name on all browsers, for instance, unless you
> completely split it out from the line calling Class.create).  And if
> you're interested in more about truly private methods, I wrote up
> something about those recently[2].
>
> [1]http://yura.thinkweb2.com/named-function-expressions/
> [2]http://blog.niftysnippets.org/2009/09/private-methods-in-javascript.html
>
> HTH,
> --
> T.J. Crowder
> tj / crowder software / comwww.crowdersoftware.com
>
> On Sep 22, 7:33 pm, Chris P <haroldthehun...@gmail.com> wrote:
>
>
>
> > Hello,
>
> > I'm trying to create a JS class and I'm having some trouble with the
> >functionscope.  I have afunctionwithin one of the class methods
> > that is trying to call another class method, but I'm not sure the
> > right way to call it.  Here is an example:
>
> >                 var Foo = Class.create();
> >                         Foo.prototype = {
> >                                 initialize:function(params) {
> >                                         alert('you just created an object, 
> > good work.');
> >                                 },
> >                                 functionA:function(){
> >                                         functionAA();
> >                                        functionfunctionAA(){
> >                                                 this.functionB();
> >                                                 
> > //this.functionB.bind(this);- also didn't work
> >                                         }
> >                                 },
> >                                 functionB:function(){
> >                                         alert('functionb');
> >                                 }
> >                         }
>
> >                 Event.observe(window, 'load',function() {
> >                         var bar = new Foo();
> >                         bar.functionA();
> >                 });
>
> > Any help would be appreciated!
>
> > Thanks,
> > --Chris
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Prototype & script.aculo.us" group.
To post to this group, send email to prototype-scriptaculous@googlegroups.com
To unsubscribe from this group, send email to 
prototype-scriptaculous+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/prototype-scriptaculous?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to