Getting the 'this' scope of the caller

P

Phrogz

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!
 
P

Phrogz

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. :)
 
V

VK

Phrogz said:
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?
 
P

Phrogz

VK said:
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
 
R

runsun pan

Phrogz said:
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 ?
 
P

Phrogz

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
 
M

Michael Winter

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
 
P

Phrogz

Michael said:
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
 
M

Michael Winter

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
 
V

VK

Phrogz said:
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>
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,898
Latest member
BlairH7607

Latest Threads

Top