binding member functions

J

Jens Thiele

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();
 
D

Douglas Crockford

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

Richard Cornford

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.
 
J

Jens Thiele

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?
 
R

Richard Cornford

Jens Thiele wrote:
... . 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.
 
J

Jens Thiele

Richard said:
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:
<[email protected]>
and
http://bugzilla.mozilla.org/show_bug.cgi?id=240577

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

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)
 

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,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top