Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Javascript (http://www.velocityreviews.com/forums/f68-javascript.html)
-   -   binding member functions (http://www.velocityreviews.com/forums/t876665-binding-member-functions.html)

Jens Thiele 04-13-2004 03:06 PM

binding member functions
 
i want to bind a member function
i hope this example will explain what i want to do
(it seems my solution works - but is there a better way?)

// first a simple function variable example
function foo(){
print("foo");
}
// prints foo and foo
function simpleTest(){
foo();
var x=foo;
x();
}
simpleTest();

// now i want to bind a member function to a function variable
// this will not work:
// x=foo.bar; (since "this" will be wrong)

function Foo(bar){
this.b=bar;
}
Foo.prototype.bar=function()
{
print (this.b);
}

function test() {
var foo=new Foo("bar");
foo.bar(); // print bar

// bind method - is there a simpler way ????
var x=function(y){var z=y;return function(){z.bar();}}(foo);

x(); // print bar
}

test();


Douglas Crockford 04-13-2004 04:06 PM

Re: binding member functions
 
Jens Thiele wrote:

> i want to bind a member function
> i hope this example will explain what i want to do
> (it seems my solution works - but is there a better way?)
>
> // first a simple function variable example
> function foo(){
> print("foo");
> }
> // prints foo and foo
> function simpleTest(){
> foo();
> var x=foo;
> x();
> }
> simpleTest();
>
> // now i want to bind a member function to a function variable
> // this will not work:
> // x=foo.bar; (since "this" will be wrong)
>
> function Foo(bar){
> this.b=bar;
> }
> Foo.prototype.bar=function()
> {
> print (this.b);
> }
>
> function test() {
> var foo=new Foo("bar");
> foo.bar(); // print bar
>
> // bind method - is there a simpler way ????
> var x=function(y){var z=y;return function(){z.bar();}}(foo);
>
> x(); // print bar
> }
>
> test();


function bind(o, f) {
if (!isFunction(f)) {
f = o[f];
}
return isFunction(f) ? function () {
return f.apply(o, arguments);
} : null;
}

var x = bind(foo, 'bar');

http://www.crockford.com/javascript/remedial.html

Richard Cornford 04-13-2004 04:14 PM

Re: binding member functions
 
Jens Thiele wrote:
<snip>

So what you want to do is call a function as a method of a particular
object instance, using a reference to a function.

> function test() {
> var foo=new Foo("bar");
> foo.bar(); // print bar
>
> // bind method - is there a simpler way ????
> var x=function(y){var z=y;return function(){z.bar();}}(foo);

<snip>

That is the pattern that works, but could be simpler as function
parameters act much like local variables (they both exist as named
properties of the Activation/Variable object belonging to the execution
context of their containing function call). So the code could be
simplified to:-

var x=function(y){return function(){y.bar();}}(foo);

Or made in to a general external function with a consequent reduction in
the number of function objects that need to be created during the
process. For example:-

function associateObjWithEvent(obj, methodName){
return (function(e){
e = e||window.event;
return obj[methodName](e, this);
});
}

- is a version I use for associating objects with DOM element event
handlers. It might be called in your context as:-

var x = associateObjWithEvent(foo, 'bar');

Though it was designed to have the returned function assigned to an
event handling property, so normal use would be:-

elRef.onclick = associateObjWithEvent(foo, 'bar');

- but you don't seem to want the parameter handling in the method call,
or the flexibility in method names used. So maybe:-

function associateObjWithBarCall(obj){
return (function(){
obj.bar();
});
}

- used as:-

var x = associateObjWithBarCall(foo);

Richard.



Jens Thiele 04-14-2004 01:55 PM

Re: binding member functions
 
> That is the pattern that works, but could be simpler as function
> parameters act much like local variables (they both exist as named
> properties of the Activation/Variable object belonging to the execution
> context of their containing function call). So the code could be
> simplified to:-
>
> var x=function(y){return function(){y.bar();}}(foo);


Ah!
I still have many misconceptions about the language it seems.
It is indeed quite similar to scheme. But now I do not understand why
the following happens.

x=function(text){return function(){print(text);};}("hello world");
x(); // will print hello world as expected
foo={x:1,y:2}
foo.watch("x",x);
foo.x=10; // will print undefined and not hello world

i don't quite understand this
it seems in the second case the context is different

i tried the following:
x=function(text){return
function(){print("this:"+this);print("this.foomem: "+this.y);print("text:"+text);};}("hello
world");
x();
foo={x:1,foomem:2}
foo.watch("x",x);
foo.x=10;

output:
this:[object global]
this.foomem:undefined
text:hello world
this:[object Object]
this.foomem:undefined
text:undefined

=> the first time this is the global object the second time it is foo
does it matter?
text is not in the global object

I get something wrong here
any help?


Richard Cornford 04-15-2004 12:44 AM

Re: binding member functions
 
Jens Thiele wrote:
<snip>
> ... . But now I do not understand why
> the following happens.
>
> x=function(text){return function(){print(text);};}("hello world");
> x(); // will print hello world as expected
> foo={x:1,y:2}
> foo.watch("x",x);


You have stumbled into a grey area here, the - watch - method of objects
did not make it into ECMA 262 and so does not have a public formal
specification. So exactly how it will behave where implemented is
uncertain.

My copy of the Netscape JavaScript 1.4 documentation says that -
obj.watch('propName', handler) - will call the - handler - when an
assignment is made to the property of - obj - with the specified name.
Calling it as - handler('propName' oldval, newval) and assigning the
function's return value to the property of - obj.

If the handler is an inner function then it should still have the
Activation/Variable object of its outer function's execution context in
its scope chain when it is called.

> foo.x=10; // will print undefined and not hello world
>
> i don't quite understand this
> it seems in the second case the context is different
>
> i tried the following:
> x=function(text){return
> function(){print("this:"+this);print("this.foomem: "+this.y);
> print("text:"+text);};}("hello world");
> x();
> foo={x:1,foomem:2}
> foo.watch("x",x);
> foo.x=10;
>
> output:
> this:[object global]


So which javascript implementation are you using? I don''t recall the
toString method of a global object ever returning that particular string
(not that I look at that string value often).

> this.foomem:undefined
> text:hello world


That all conforms to my expetations.

> this:[object Object]


But this is interesting, for the - this - keyword to refer to an object
the handler must have been called as a method of that object, and the
Netscape JavaScript 1.4 documentation does not mention that at all, the
expectation would be that - this - remained a reference to the global
object.

> this.foomem:undefined


However, the object that is being referred to by - this - is not the
object on which the - watch - method was called, else - this.foomem -
would not be undefined.

> text:undefined


By ECMA specification the only way that the - text - property of the
Activation/Variable object from the execution context of the outer
function, on the scope chain of the inner function, could be returning -
undefined - in the inner function is if an object higher in the scope
chain for the inner function's execution context had a defined property
named - text - that had an undefined value, masking the original -
text - parameter property.

It is not impossible for an implementation to add objects to the scope
chain of a function call, it is even common with event handling
functions generated from HTML attribute strings, but there doesn't seem
to be any reason for doing so in the context of - watch.

On the other hand, without a formal specification for - watch - it is
impossible to say what it should be doing when executing - handler -, it
could call - handler - as a method of some arbitrary object, and add any
number of unknown objects to the scope chain for the function call. And
nobody could say it was wrong for doing so.

> => the first time this is the global object the second time it is foo


It is not - foo - else - this.foomem - would return the value 2 rather
than undefined.

> does it matter?
> text is not in the global object
>
> I get something wrong here


You may be wrong to have expectation of methods outside of the ECMA
specification, though the behaviour of the implementation you are using
is certainly unexpected.

> any help?


There can be no help without knowing what you are trying to achieve.

Richard.



Jens Thiele 04-15-2004 01:05 PM

Re: binding member functions
 
Richard Cornford wrote:
> You have stumbled into a grey area here, the - watch - method of objects
> did not make it into ECMA 262 and so does not have a public formal
> specification. So exactly how it will behave where implemented is
> uncertain.


it should get into ECMA - it is really useful ;-)

> My copy of the Netscape JavaScript 1.4 documentation says that -
> obj.watch('propName', handler) - will call the - handler - when an
> assignment is made to the property of - obj - with the specified name.
> Calling it as - handler('propName' oldval, newval) and assigning the
> function's return value to the property of - obj.
>
> If the handler is an inner function then it should still have the
> Activation/Variable object of its outer function's execution context in
> its scope chain when it is called.


yes this was my problem
with the example below I tried to understand why it does not work.
I am using spidermonkey - and this was a bug which is now fixed
(thanks to brendan)
see also:
<407E34E1.5080207@meer.net>
and
http://bugzilla.mozilla.org/show_bug.cgi?id=240577

>>foo.x=10; // will print undefined and not hello world
>>
>>i don't quite understand this
>>it seems in the second case the context is different
>>
>>i tried the following:
>>x=function(text){return
>>function(){print("this:"+this);print("this.foome m:"+this.y);


i had a bug here ^
this should be this.fooomem and not this.y
sorry for this one

> So which javascript implementation are you using? I don''t recall the
> toString method of a global object ever returning that particular string
> (not that I look at that string value often).


I am using spidermonkey and embedding it.

>>this.foomem:undefined
>>text:hello world

>
>
> That all conforms to my expetations.


yep to mine, too


>>this:[object Object]

>
>
> But this is interesting, for the - this - keyword to refer to an object
> the handler must have been called as a method of that object, and the
> Netscape JavaScript 1.4 documentation does not mention that at all, the
> expectation would be that - this - remained a reference to the global
> object.
>
>
>>this.foomem:undefined

>
>
> However, the object that is being referred to by - this - is not the
> object on which the - watch - method was called, else - this.foomem -
> would not be undefined.


this was my mistake see above (i do print this.y instead of this.foomem)
- sorry again


>>I get something wrong here

>
>
> You may be wrong to have expectation of methods outside of the ECMA
> specification, though the behaviour of the implementation you are using
> is certainly unexpected.


okay with the patch to spidermonkey, everthing works like expected
for the record (the script without my bug and the output of a fixed
spidermonkey version):

// first test
print ("First test output");
x=function(text){return function(){print(text);};}("hello world");
x(); // will print hello world as expected
foo={x:1,y:2}
foo.watch("x",x);
foo.x=10; // now will print hello world

// second test
print ("Second test output");
x=function(text){
return function(){
print("this:"+this);
print("this.foomem:"+this.foomem);
print("text:"+text);
};
}("hello world");
x();
foo={x:1,foomem:2}
foo.watch("x",x);
foo.x=10;

First test output
hello world
hello world
Second test output
this:[object global]
this.foomem:undefined
text:hello world
this:[object Object]
this.foomem:2
text:hello world

thanks
i am really happy now
(it works, and more important to me, my expectations were reasonable,
I thought that I still did not understand some basic language concept)



All times are GMT. The time now is 09:49 PM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.