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 use Function#call (or Function#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);
        function functionAA() {
            this.functionB();
        }
    },

    functionB: function() {
        alert('function b');
    }
});
* * * *

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
uses Function#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);
        function functionAA(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();
        function functionAA() {
            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
scoping function (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(){

    function initialize(params) {
        alert('you just created an object, good work.');
    }

    function functionA() {
        functionAA.call(this);
        function functionAA() {
            this.functionB();
        }
    }

    function functionB() {
        alert('function b');
    }

    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 scoping function,
declare our functions within it in the normal way, and then have that
scoping function return an object that maps properties to the named
functions (the "initialize" property refers to the "initialize"
function, etc.).  We then execute the anonymous function immediately,
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 anonymous function called by the 'load'
event.)  Whereas with the earlier version using mostly anonymous
functions, the call stack would look like this:
    (?)
    (?)
    functionAA
    (?)

...because the only function with an actual name in the entire stack
is functionAA.

A second advantage of this scoping function is 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(){

    function initialize(params) {
        alert('you just created an object, good work.');
    }

    function functionA() {
        functionAA.call(this);
        function functionAA() {
            this.functionB();
        }
    }

    function functionB() {
        alert('function b');
        trulyPrivate();
    }

    function trulyPrivate() {
        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 named
function expressions[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 scoping function a 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 / com
www.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
> function scope.  I have a function within 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();
>                                         function functionAA(){
>                                                 this.functionB();
>                                                 //this.functionB.bind(this);- 
> also didn't work
>                                         }
>                                 },
>                                 functionB: function (){
>                                         alert('function b');
>                                 }
>                         }
>
>                 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