Cracking prototype.js

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Elf M. Sternberg

    Cracking prototype.js


    One of the complaints about prototype.js (google for it if you're not
    familiar with it) is that it's poorly documented. I have this inkling
    that the key to understanding prototype.js is in the bind function.

    The problem with Javascript is that the "this" operator is poorly
    overloaded and it is often hard to understand in the context of
    object-oriented javascript

    So, let's start with the definition:

    Function.protot ype.bind = function(object ) {
    var method = this;
    return function() {
    method.apply(ob ject, arguments);
    }
    }

    As I read this, it states that all functions (which are themselves
    objects) will, in the future, have an associated method called "bind".
    The function() function, so to speak, simply instantiates a Function
    object with the parameter list and then evals the statement, sticking
    the resulting execution-tree in the current code frame.

    The "this" there refers to the function object associated with the call
    to bind(), right? But the word "arguments" there refers to the
    arguments passed to the function object *generated by* the call to
    bind().

    In every example within prototype.js, bind() is called either in a
    constructor or within a method contexted to a javascript object, and is
    always called with "this" as its argument, e.g.:

    this.options.on Complete = this.updateCont ent.bind(this);

    As I read the code it seems to be stating that the "this" object
    referred to within bind()'d functions are being coerced into always
    referring to the current instantiated object.

    But isn't this always the case anyway? Is this a rather confusing
    attempt to ensure "this" purity whereby the call

    method.apply(ob ject, arguments)

    is forced to always have the reference to the containing object present?

    I think I've got it. Bind() generates uniq functions that contain live
    references to the objects to which they belong, such that the function
    object can then be passed to setTimeout() or onMouseOver(), handlers
    that accept functions but not objects.

    Have I got this right?

    Elf
  • Yann-Erwan Perio

    #2
    Re: Cracking prototype.js

    Elf M. Sternberg wrote:
    [color=blue]
    > One of the complaints about prototype.js (google for it if you're not
    > familiar with it) is that it's poorly documented. I have this inkling
    > that the key to understanding prototype.js is in the bind function.[/color]

    I didn't know the file you refer too, at first sight it looks promising
    and deserves more time - exciting moments for next Sunday:-)
    [color=blue]
    > Function.protot ype.bind = function(object ) {
    > var method = this;
    > return function() {
    > method.apply(ob ject, arguments);
    > }
    > }[/color]

    An excellent example which should illustrate perfectly how javascript
    handles prototypes and closures.
    [color=blue]
    > As I read this, it states that all functions (which are themselves
    > objects) will, in the future, have an associated method called "bind".[/color]

    Indeed; this is permitted by javascript prototypes.

    In javascript, functions can be executed in two manners:
    - in the "constructo r way", when called with the "new" keyword,
    - in the "call way", when called without the "new" keyword.

    When called as a constructor, a function executes the following steps
    (roughly):
    - create an empty object, which will be the instance, and which will be
    referred to "this" in the function's body that's going to be executed,
    - associate a prototype to this instance; normally the link to this
    prototype is not enumerable on the instance (this is the Ecma
    [[prototype]] property, made accessible in Mozilla under the "__proto__"
    property"); the prototype associated is a simple object, which can be
    defined by the programmer with the "prototype" property of the
    *constructor* (this property not being the prototype of the constructor
    itself, but truly the prototype to be associated to instances
    constructed by the constructor),
    - execute the function's body, as in a "call way".

    Each function is, in javascript, an object, created by a regular
    constructor called "Function". This means that each function has an
    "internal link" __proto__ to a unique object, Function.protot ype.

    When we call foo.bind(), the identifier resolution will first look at
    properties defined for the function "foo"; since it has no "bind"
    property, it will look up the prototype and search the property on the
    prototype; if the prototype does not possess the property, then the
    identifier will be searched on the prototype's prototype, and so on...
    until the ultimate prototype (null) is reached - this is the "prototype
    chain". When the "bind" function is found, it is executed, and the
    "this" value associated to the execution context is the function/object
    "foo".

    ---
    function Foo(){}
    Foo.prototype.b ar=42;

    var f1=new Foo();
    var f2=new Foo();

    f1.bar=50;

    alert(f1.bar); //50
    alert(f2.bar); //42

    Foo.prototype.b ar=70;
    alert(f1.bar); //50
    alert(f2.bar); //70

    delete f1.bar; //remove the property on f1
    alert(f1.bar); //70
    ---

    More information on prototypes can be found in the ECMAScript
    specification ECMA262-3, §15.
    [color=blue]
    > The "this" there refers to the function object associated with the call
    > to bind(), right? But the word "arguments" there refers to the
    > arguments passed to the function object *generated by* the call to
    > bind().[/color]

    That's right; the code you've provided nicely exploits the way functions
    work in javascript:
    - functions can be created in several ways, either using the Function
    constructor, or using function expressions; the way a function is
    created is important in regards of the scope it will have access to,
    - when creating a function using a function expression/declaration, we
    can nest functions inside functions; an inner function will have access
    to the "container function" variables, even if the "container function"
    has been executed and has returned (closures / lexical scoping),
    - the "this" value available in a function depends on the way the
    function is called; it is either the global object if the function is
    called directly, or the instance to which the function is bound as a
    method, if called as a method; the "this" value can even be set by the
    programmer, using "apply" or "call".

    More information on Function.protot ype.apply can be found in the
    ECMAScript specification, ECMA262-3, §15.3.4.3.

    More information on the "this" value, "Arguments" object, and more
    generally execution contexts, can be found in the ECMAScript
    specification, ECMA262-3, §10.
    [color=blue]
    > In every example within prototype.js, bind() is called either in a
    > constructor or within a method contexted to a javascript object, and is
    > always called with "this" as its argument, e.g.:
    >
    > this.options.on Complete = this.updateCont ent.bind(this);
    >
    > As I read the code it seems to be stating that the "this" object
    > referred to within bind()'d functions are being coerced into always
    > referring to the current instantiated object.
    >
    > But isn't this always the case anyway? Is this a rather confusing
    > attempt to ensure "this" purity whereby the call
    >
    > method.apply(ob ject, arguments)
    >
    > is forced to always have the reference to the containing object present?[/color]

    Well, if the author always passes "this" to the "bind" calls then indeed
    there's no added value, the "binding" process is useless, brings some
    confusion and even gets you a slight memory overhead.

    However I can understand the will of the author to have something
    generic enough, so that the function can be used with other "this"
    values (it's possibly better to define the "this" value explicitly in
    the call rather than calling the bind() function in a context with an
    appropriate "this" value).
    [color=blue]
    > I think I've got it. Bind() generates uniq functions that contain live
    > references to the objects to which they belong, such that the function
    > object can then be passed to setTimeout() or onMouseOver(), handlers
    > that accept functions but not objects.
    >
    > Have I got this right?[/color]

    Definitely yes!

    Reading the following should clear all your doubts:
    <URL:http://www.jibbering.c om/faq/faq_notes/closures.html>


    Regards,
    Yep.

    Comment

    • Elf M. Sternberg

      #3
      Re: Cracking prototype.js

      Yann-Erwan Perio <yep@invalid.co m> writes:
      [color=blue]
      > Elf M. Sternberg wrote:
      >[color=green]
      > > One of the complaints about prototype.js (google for it if you're not
      > > familiar with it) is that it's poorly documented. I have this inkling
      > > that the key to understanding prototype.js is in the bind function.[/color]
      >
      > I didn't know the file you refer too, at first sight it looks
      > promising and deserves more time - exciting moments for next Sunday:-)[/color]

      Thank you so much for the confirmation and links! Yes, I
      stumbled upon prototype.js a couple of weeks ago; it's an
      language-agnostic version of the toolkit used for Ruby On Rails, but it
      is not at all document, and I've been struggling to understand it. If
      you think *this* is promising, you should see what they do with it:



      And there are mind-boggling special visual effects achieved with
      it:



      Both are under a very generous license. (Hence my effort to
      understand it; I'd like to understand it enough to both use it and
      contribute back documentation.)

      Elf

      Comment

      • VK

        #4
        Re: Cracking prototype.js

        I guess the inspiration idea of this library was to overcome "this"
        discrepancy in IE.

        Consider this intrinsic event capturer:

        (1)
        ....
        <div id="div1" onclick="someFu nctionFor(this) ">
        ....
        Here "this" will point nicely to the event source (div1), so we can
        forward it to the event handler someFunctionFor ().

        I couldn't find an official term for a code located within <script>
        tags as opposed to intrinsic code within an element tag, so lets us
        call it "scriptbode d code" (you are very welcome for a better
        term).
        So consider now this scriptboded code:

        (2)
        ....
        someFunctionFor (e) {
        /* In IE "this" points now to the current window!
        Luckily we still have event.srcElemen t property
        to determine the actual event capturer (div1)
        */
        }

        document.getEle mentById('div1' ).onclick = someFunctionFor ;
        ....

        But consider this HTML code:
        <div id="div1">DIV 1
        <span id="span1">SPA N 1
        <a href="foo.html" >LINK</a>
        </span>
        </div>

        In this situation div1 will receive any events not only for itself, but
        also for all underlying elements (span & a).
        event.srcElemen t in (2) is out of use, because it will point to the
        most underlying element for the event bubble (it can be div, span or a
        depending on the actual place you clicked/pressed etc.)

        The obvious solution would be to use function wrapper (closure in
        JavaScript term):
        document.getEle mentById('div1' ).onclick =
        function(){some FunctionFor(thi s);};

        This brings us though to the infamous circular reference memory leak
        bug in IE:
        <http://support.microso ft.com/default.aspx?sc id=kb;EN-US;830555>

        I'm sorry for being so multiworded but the situation requires all
        these preliminary explanations. I see that "this" discrepancy is too
        "rude" and obvious to be a bug. It rather seems like "this"
        functionality was intentionally half-killed in IE to escape that memory
        leak. It might be too deepcoded into the program body to be easily
        fixed.

        I'm afraid that prototype.js is really a further development of
        document.getEle mentById('div1' ).onclick =
        function(){some FunctionFor(thi s);};

        It's still a closure but moved on the prototype level which makes it
        even more dangerous for memory management.

        I cannot tell for sure without profound test, but prototype.js on IE
        will be a source of a huge amount of uncollectible trash (means objects
        cannot be removed by gc).

        Comment

        • VK

          #5
          Re: Cracking prototype.js

          A further confirmation of my guess could be found in the fact that IE
          event model has fromElement and toElement properties but they are set
          only for mouseout/mouseover events. Instead of rectify this (as well as
          solving the "this" problem) they introduced in the latter releases
          document.active Element (global event capturer) and obj.setActive()
          method. So in the case of nested elements they have proposed an option
          to track targets using from/toElement and to set activeElement
          accordingly.
          This gives us two possible explanation:

          1) MS programmers do not know how to programm
          - Sorry, but *not* true, whatever you may think about MS (M$)

          2) MS run into an evangelism problem (something lying into the very
          core of their OS) and they really don't know how to pull out of this on
          the browser level.
          Evidently in the latter case no one JavaScript patch can help. (They
          would make an .exe IE patch long time ago).

          Comment

          • Thomas 'PointedEars' Lahn

            #6
            Re: Cracking prototype.js

            Elf M. Sternberg wrote:
            [color=blue]
            > One of the complaints about prototype.js (google for it if you're not
            > familiar with it) is that it's poorly documented.[/color]

            It is not only poorly documented but most certainly based on a (common?)
            misconception. What should an "object-oriented Javascript library" be?
            J(ava)Script/ECMAScript *are* object-oriented programming languages;
            that JavaScript < 2.0 (v2.0 is only available in the Epimetheus test
            implementation) , JScript < 7.0 (v7.0 in .NET framework only) and
            ECMAScript < 4 (Ed. 4 is still only a Netscape proposal) only provide
            for prototype-based inheritance does not diminish neither their status
            nor that of other OOP languages using the same object model.

            While emulating class-based inheritance in a prototype-based language
            may be interesting, the approach taken here is (at least to me) somewhat
            bogus. Instead of using proper function declarations for constructors,
            an object is created using a previously created prototype with
            Class.create(), which in turn requires an `initialize' property that
            requires the Function.protot ype.apply() method to be called, which is
            modified later. I can see no purpose or advantage in that other than
            code obfuscation (if this can be even called an advantage); it reduces
            code efficiency and increases the average memory footprint compared to
            calling a constructor function (which can do the initialization) with
            the `new' keyword.


            PointedEars

            Comment

            • commercial

              #7
              Re: Cracking prototype.js

              I have never used "Class" or "initialize ".

              Once you posted code instead of
              "chopped liver academia"
              you will have started to learn.

              var myStuff = function(){retu rn "when I need it"}

              When you go up and down the tree of a language,
              you want to harvest, not beiing sucked by the juice.



              "Thomas 'PointedEars' Lahn" <PointedEars@we b.de> wrote in message
              news:2377616.bz k5MvnWfF@Pointe dEars.de...[color=blue]
              > Elf M. Sternberg wrote:
              >[color=green]
              >> One of the complaints about prototype.js (google for it if you're not
              >> familiar with it) is that it's poorly documented.[/color]
              >
              > It is not only poorly documented but most certainly based on a (common?)
              > misconception. What should an "object-oriented Javascript library" be?
              > J(ava)Script/ECMAScript *are* object-oriented programming languages;
              > that JavaScript < 2.0 (v2.0 is only available in the Epimetheus test
              > implementation) , JScript < 7.0 (v7.0 in .NET framework only) and
              > ECMAScript < 4 (Ed. 4 is still only a Netscape proposal) only provide
              > for prototype-based inheritance does not diminish neither their status
              > nor that of other OOP languages using the same object model.
              >
              > While emulating class-based inheritance in a prototype-based language
              > may be interesting, the approach taken here is (at least to me) somewhat
              > bogus. Instead of using proper function declarations for constructors,
              > an object is created using a previously created prototype with
              > Class.create(), which in turn requires an `initialize' property that
              > requires the Function.protot ype.apply() method to be called, which is
              > modified later. I can see no purpose or advantage in that other than
              > code obfuscation (if this can be even called an advantage); it reduces
              > code efficiency and increases the average memory footprint compared to
              > calling a constructor function (which can do the initialization) with
              > the `new' keyword.
              >
              >
              > PointedEars[/color]

              Comment

              • Thomas 'PointedEars' Lahn

                #8
                Re: Cracking prototype.js

                commercial wrote:
                [color=blue]
                > I have never used "Class" or "initialize ".[/color]

                I did not state any of the kind.
                [color=blue]
                > Once you posted code instead of
                > "chopped liver academia"
                > you will have started to learn.[/color]

                Once you posted a non-intended-to-be-insulting, meaningful response that
                refers to the actual text written before, which shows you have read and
                understand the newsgroup's charta and FAQ, you will have started to think.
                Until then, you want to stay away from the input devices within your reach.
                You have been warned.
                [color=blue]
                > [senseless reply including full top post quote snipped][/color]


                PointedEars

                Comment

                • Thomas 'PointedEars' Lahn

                  #9
                  Re: Cracking prototype.js

                  commercial wrote:
                  [color=blue]
                  > I have never used "Class" or "initialize ".[/color]

                  I did not state any of the kind.
                  [color=blue]
                  > Once you posted code instead of
                  > "chopped liver academia"
                  > you will have started to learn.[/color]

                  Once you posted a non-intended-to-be-insulting, meaningful response that
                  refers to the actual text written before, which showed you have read and
                  understood the newsgroup's charta and FAQ, you will have started to think.
                  Until then, you want to stay away from the input devices within your reach.
                  You have been warned.
                  [color=blue]
                  > [senseless reply including full top post quote snipped][/color]


                  PointedEars

                  Comment

                  Working...