Getting the 'this' scope of the caller

Discussion in 'Javascript' started by Phrogz, Oct 11, 2006.

  1. Phrogz

    Phrogz Guest

    I'm trying to write an 'each' function for a JavaScript array that
    behaves like Ruby's Array#each. (It doesn't matter if you know Ruby to
    help with this question.)

    My problem is the scope of 'this' inside the iterator callback. I would
    like it to be the same as the object that called the each() on the
    array. Right now I have to do that with a closure or an
    explicitly-passed 'this' scope. For example:

    function Person( inName, inCats ) {
    this.name = inName;
    this.cats = inCats;
    }

    // Using a closure
    Person.prototype.showInfo = function( ) {
    var me = this;
    this.cats.each( function( catName ){
    alert( me.name + " owns " + catName );
    } );
    }

    Array.prototype.each = function( inCallback ){
    for ( var i=0,len=this.length; i<len; ++i ){
    inCallback( this[ i ], i );
    }
    }

    phrogz = new Person( 'Gavin', [ 'Fuzzles', 'Kitty' ] );
    phrogz.showInfo( );
    --> Gavin owns Fuzzles
    --> Gavin owns Kitty


    // Using an explicit scope
    Person.prototype.showInfo = function( ) {
    this.cats.each( this, function( catName ){
    alert( this.name + " owns " + catName );
    } );
    }

    Array.prototype.each = function( inScope, inCallback ){
    for ( var i=0,len=this.length; i<len; ++i ){
    inCallback.call( inScope, this[ i ], i );
    }
    }


    Inside the each() function, arguments.callee.caller would give me a
    reference to the showInfo function object. What I am looking for is a
    way to access the scope of the 'this' receiver within that particular
    invocation of showInfo(), so that I can use it in place of inScope
    without having to pass 'this' each call.


    Thanks in advance for any help!
     
    Phrogz, Oct 11, 2006
    #1
    1. Advertising

  2. Phrogz

    Phrogz Guest

    I forgot to add (in case it matters): this is being executed by (a
    slightly modified version of) the SpiderMonkey runtime. And no, I don't
    have ability to change the runtime to add this capability. :)
     
    Phrogz, Oct 11, 2006
    #2
    1. Advertising

  3. Phrogz

    VK Guest

    Phrogz wrote:
    > I'm trying to write an 'each' function for a JavaScript array that
    > behaves like Ruby's Array#each. (It doesn't matter if you know Ruby to
    > help with this question.)


    Actually it does help a lot: because it is difficult to help to write a
    method w/o knowing its objectives. I've missed the rails of Ruby :)
    but latest Gecko (including Firefox 1.5 or higher) has forEach method
    added for Array. Please take a look at:
    <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach>
    Is it what you want for all other UA's? If not then what is
    missing/superfluous?
     
    VK, Oct 11, 2006
    #3
  4. Phrogz

    Phrogz Guest

    VK wrote:
    > Phrogz wrote:
    > > I'm trying to write an 'each' function for a JavaScript array that
    > > behaves like Ruby's Array#each. (It doesn't matter if you know Ruby to
    > > help with this question.)

    >
    > Actually it does help a lot: because it is difficult to help to write a
    > method w/o knowing its objectives. I've missed the rails of Ruby :)
    > but latest Gecko (including Firefox 1.5 or higher) has forEach method
    > added for Array. Please take a look at:
    > <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach>
    > Is it what you want for all other UA's? If not then what is
    > missing/superfluous?


    What is listed there is what I have implemented (except that the order
    of the callback/scope parameters are reversed).

    What I would like is a forEach type method where the 'this' scope used
    inside the callback is the same as the 'this' scope that was used to
    invoke the forEach. For example:

    G = {}
    G.a = this;
    myArray.each( function(){ G.b = this } );

    I would like G.a===G.b
     
    Phrogz, Oct 12, 2006
    #4
  5. Phrogz

    runsun pan Guest

    Phrogz wrote:
    > G = {}
    > G.a = this;
    > myArray.each( function(){ G.b = this } );
    >
    > I would like G.a===G.b


    I am still confusing (the above example seems to make it more
    confusing).

    Let met put it this way:

    someone = new Person("abc", ['d','e'])
    someone.cats.forEach( function() { alert(X)} )

    You want X to be "someone", right?

    That is, cats' this, but not forEach's this. Is that what you want ?
     
    runsun pan, Oct 12, 2006
    #5
  6. Phrogz

    Phrogz Guest

    I am in turn confused by your example. :/

    Let me try one more time:

    function Person( inName, inCats ) {
    this.name = inName;
    this.cats = inCats;
    }

    Person.prototype.showCats = function( ) {
    this.cats.each( function( catName ) {
    alert( this.name + ' owns ' + catName );
    } );
    }

    phrogz = new Person( 'Gavin', [ 'Fuzzles', 'Kitty' ] );
    phrogz.showCats( );

    I am hoping to find a way to write the Array.prototype.each() function
    so that the above code outputs:
    --> Gavin owns Fuzzles
    --> Gavin owns Kitty
     
    Phrogz, Oct 12, 2006
    #6
  7. Phrogz wrote:

    [snip]

    > function Person( inName, inCats ) {
    > this.name = inName;
    > this.cats = inCats;
    > }
    >
    > Person.prototype.showCats = function( ) {
    > this.cats.each( function( catName ) {
    > alert( this.name + ' owns ' + catName );
    > } );
    > }
    >
    > phrogz = new Person( 'Gavin', [ 'Fuzzles', 'Kitty' ] );
    > phrogz.showCats( );
    >
    > I am hoping to find a way to write the Array.prototype.each() function
    > so that the above code outputs:
    > --> Gavin owns Fuzzles
    > --> Gavin owns Kitty


    That isn't going to happen without modifying the showCats method, and
    you've already posted the two most likely approaches.

    If the each function is to be a method of the Array prototype object, it
    is expected that the this operator will refer to an array (or at least
    an object with a length property and numeric properties [0,length-1]).
    The only other objects it can know about must either be in its scope
    chain, or its argument list. The scope chain is fixed when the method is
    created so that isn't an option, and if the argument list is
    unacceptable then responsibility must fall to the callback to retain any
    necessary information.

    Mike
     
    Michael Winter, Oct 12, 2006
    #7
  8. Phrogz

    Phrogz Guest

    Michael Winter wrote:
    > Phrogz wrote:
    > > I am hoping to find a way to write the Array.prototype.each() function
    > > so that the above code outputs:
    > > --> Gavin owns Fuzzles
    > > --> Gavin owns Kitty

    >
    > That isn't going to happen without modifying the showCats method, and
    > you've already posted the two most likely approaches.


    Thanks for the confirmation. I was hoping that there might be some
    feature (in ECMAScript or SpiderMonkey specifically) that allowed you
    to access the receiver that invoked a method as part of the call stack
    browsing.

    It's not altogether surprising that it doesn't exist, but would
    certainly be nice in a situation like this. Oh well.

    (It looks[1] like SpiderMonkey might have had this at one time as a
    "__caller__" property, but it was yanked. It's not in my (older)
    SpiderMonkey release, unfortunately.)

    [1]
    http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Function:caller
     
    Phrogz, Oct 12, 2006
    #8
  9. Phrogz wrote:

    [snip]

    > I was hoping that there might be some feature (in ECMAScript or
    > SpiderMonkey specifically) that allowed you to access the receiver
    > that invoked a method as part of the call stack browsing. It's not
    > altogether surprising that it doesn't exist, but would certainly be
    > nice in a situation like this. Oh well.


    It's not entirely unusual, either: the issue also arises when attempting
    to assign a method of some object as an event listener. When the
    listener is invoked, the this operator will refer to the element to
    which the listener is attached, not the object from which the listener
    originated. The general solution is much the same: use a closure and
    store a reference to the object in the scope chain of that function.

    [snip]

    Mike
     
    Michael Winter, Oct 12, 2006
    #9
  10. Phrogz

    VK Guest

    Phrogz wrote:
    > What I would like is a forEach type method where the 'this' scope used
    > inside the callback is the same as the 'this' scope that was used to
    > invoke the forEach. For example:
    >
    > G = {}
    > G.a = this;
    > myArray.each( function(){ G.b = this } );
    >
    > I would like G.a===G.b


    I'm not using prototype for such tricky things, and sure you are not
    doing it to alert cats' names :) so depending on the real purpose it
    can be not suitable. But withing the spelled requirements I would do it
    this way (keeping all methods as static):


    <html>
    <head>
    <title>Cats</title>
    <meta http-equiv="Content-Type"
    content="text/html; charset=iso-8859-1">
    <script type="text/javascript">

    function Person(inName, inCats) {
    this.name = inName;
    this.cats = inCats;
    this.cats.each = each;
    this.cats.context = this;
    this.showCats = Person.showCats;
    }

    Person.showCats = function() {
    this.cats.each(alertCat);
    }

    function each(fnCallBack) {
    for (var i=0; i<this.length; i++) {
    fnCallBack.call(this.context, this);
    }
    }

    function alertCat(catName) {
    window.alert(this.name + ' has ' + catName);
    }

    phrogz = new Person('Gavin', ['Fuzzles', 'Kitty']);
    phrogz.showCats();

    </script>
    </head>

    <body>

    </body>
    </html>
     
    VK, Oct 12, 2006
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. puchacz
    Replies:
    9
    Views:
    464
    Arne Vajhøj
    Nov 30, 2010
  2. Michael Neumann

    Getting the binding of the caller

    Michael Neumann, Aug 17, 2004, in forum: Ruby
    Replies:
    9
    Views:
    190
    Michael Neumann
    Aug 18, 2004
  3. Peter Laurens
    Replies:
    11
    Views:
    193
    ara.t.howard
    Aug 3, 2007
  4. Jari Williamsson

    Create variables with caller scope

    Jari Williamsson, Jan 18, 2008, in forum: Ruby
    Replies:
    3
    Views:
    122
    Robert Klemme
    Jan 18, 2008
  5. Mark
    Replies:
    2
    Views:
    400
Loading...

Share This Page