binding member functions

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Jens Thiele

    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.b ar=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){v ar z=y;return function(){z.ba r();}}(foo);

    x(); // print bar
    }

    test();

  • Douglas Crockford

    #2
    Re: binding member functions

    Jens Thiele wrote:
    [color=blue]
    > 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.b ar=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){v ar z=y;return function(){z.ba r();}}(foo);
    >
    > x(); // print bar
    > }
    >
    > test();[/color]

    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');


    Comment

    • Richard Cornford

      #3
      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.
      [color=blue]
      > function test() {
      > var foo=new Foo("bar");
      > foo.bar(); // print bar
      >
      > // bind method - is there a simpler way ????
      > var x=function(y){v ar z=y;return function(){z.ba r();}}(foo);[/color]
      <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){r eturn function(){y.ba r();}}(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 associateObjWit hEvent(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 = associateObjWit hEvent(foo, 'bar');

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

      elRef.onclick = associateObjWit hEvent(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 associateObjWit hBarCall(obj){
      return (function(){
      obj.bar();
      });
      }

      - used as:-

      var x = associateObjWit hBarCall(foo);

      Richard.


      Comment

      • Jens Thiele

        #4
        Re: binding member functions

        > That is the pattern that works, but could be simpler as function[color=blue]
        > 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){r eturn function(){y.ba r();}}(foo);[/color]

        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(){prin t(text);};}("he llo 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(){prin t("this:"+this) ;print("this.fo omem:"+this.y); print("text:"+t ext);};}("hello
        world");
        x();
        foo={x:1,foomem :2}
        foo.watch("x",x );
        foo.x=10;

        output:
        this:[object global]
        this.foomem:und efined
        text:hello world
        this:[object Object]
        this.foomem:und efined
        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?

        Comment

        • Richard Cornford

          #5
          Re: binding member functions

          Jens Thiele wrote:
          <snip>[color=blue]
          > ... . But now I do not understand why
          > the following happens.
          >
          > x=function(text ){return function(){prin t(text);};}("he llo world");
          > x(); // will print hello world as expected
          > foo={x:1,y:2}
          > foo.watch("x",x );[/color]

          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('prop Name', handler) - will call the - handler - when an
          assignment is made to the property of - obj - with the specified name.
          Calling it as - handler('propNa me' 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.
          [color=blue]
          > 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(){prin t("this:"+this) ;print("this.fo omem:"+this.y);
          > print("text:"+t ext);};}("hello world");
          > x();
          > foo={x:1,foomem :2}
          > foo.watch("x",x );
          > foo.x=10;
          >
          > output:
          > this:[object global][/color]

          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).
          [color=blue]
          > this.foomem:und efined
          > text:hello world[/color]

          That all conforms to my expetations.
          [color=blue]
          > this:[object Object][/color]

          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.
          [color=blue]
          > this.foomem:und efined[/color]

          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.
          [color=blue]
          > text:undefined[/color]

          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.
          [color=blue]
          > => the first time this is the global object the second time it is foo[/color]

          It is not - foo - else - this.foomem - would return the value 2 rather
          than undefined.
          [color=blue]
          > does it matter?
          > text is not in the global object
          >
          > I get something wrong here[/color]

          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.
          [color=blue]
          > any help?[/color]

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

          Richard.


          Comment

          • Jens Thiele

            #6
            Re: binding member functions

            Richard Cornford wrote:[color=blue]
            > 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.[/color]

            it should get into ECMA - it is really useful ;-)
            [color=blue]
            > My copy of the Netscape JavaScript 1.4 documentation says that -
            > obj.watch('prop Name', handler) - will call the - handler - when an
            > assignment is made to the property of - obj - with the specified name.
            > Calling it as - handler('propNa me' 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.[/color]

            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.50802 07@meer.net>
            and

            [color=blue][color=green]
            >>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(te xt){return
            >>function(){pr int("this:"+thi s);print("this. foomem:"+this.y );[/color][/color]

            i had a bug here ^
            this should be this.fooomem and not this.y
            sorry for this one
            [color=blue]
            > 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).[/color]

            I am using spidermonkey and embedding it.
            [color=blue][color=green]
            >>this.foomem:u ndefined
            >>text:hello world[/color]
            >
            >
            > That all conforms to my expetations.[/color]

            yep to mine, too

            [color=blue][color=green]
            >>this:[object Object][/color]
            >
            >
            > 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.
            >
            >[color=green]
            >>this.foomem:u ndefined[/color]
            >
            >
            > 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.[/color]

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

            [color=blue][color=green]
            >>I get something wrong here[/color]
            >
            >
            > 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.[/color]

            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(){prin t(text);};}("he llo 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:"+t his);
            print("this.foo mem:"+this.foom em);
            print("text:"+t ext);
            };
            }("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:und efined
            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)

            Comment

            Working...