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 -~----------~----~----~----~------~----~------~--~---