Cracking prototype.js

Discussion in 'Javascript' started by Elf M. Sternberg, Jun 17, 2005.

  1. 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.prototype.bind = function(object) {
    var method = this;
    return function() {
    method.apply(object, 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.onComplete = this.updateContent.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(object, 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
     
    Elf M. Sternberg, Jun 17, 2005
    #1
    1. Advertising

  2. Elf M. Sternberg wrote:

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


    I didn't know the file you refer too, at first sight it looks promising
    and deserves more time - exciting moments for next Sunday:)

    > Function.prototype.bind = function(object) {
    > var method = this;
    > return function() {
    > method.apply(object, arguments);
    > }
    > }


    An excellent example which should illustrate perfectly how javascript
    handles prototypes and closures.

    > As I read this, it states that all functions (which are themselves
    > objects) will, in the future, have an associated method called "bind".


    Indeed; this is permitted by javascript prototypes.

    In javascript, functions can be executed in two manners:
    - in the "constructor 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.prototype.

    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.bar=42;

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

    f1.bar=50;

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

    Foo.prototype.bar=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.

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


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

    > 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.onComplete = this.updateContent.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(object, arguments)
    >
    > is forced to always have the reference to the containing object present?


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

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


    Definitely yes!

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


    Regards,
    Yep.
     
    Yann-Erwan Perio, Jun 18, 2005
    #2
    1. Advertising

  3. Yann-Erwan Perio <> writes:

    > Elf M. Sternberg wrote:
    >
    > > 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.

    >
    > I didn't know the file you refer too, at first sight it looks
    > promising and deserves more time - exciting moments for next Sunday:)


    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:

    http://prototype.conio.net/

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

    http://mir.aculo.us/effects/index.html

    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
     
    Elf M. Sternberg, Jun 18, 2005
    #3
  4. Elf M. Sternberg

    VK Guest

    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="someFunctionFor(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 "scriptboded 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.srcElement property
    to determine the actual event capturer (div1)
    */
    }

    document.getElementById('div1').onclick = someFunctionFor;
    ....

    But consider this HTML code:
    <div id="div1">DIV 1
    <span id="span1">SPAN 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.srcElement 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.getElementById('div1').onclick =
    function(){someFunctionFor(this);};

    This brings us though to the infamous circular reference memory leak
    bug in IE:
    <http://support.microsoft.com/default.aspx?scid=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.getElementById('div1').onclick =
    function(){someFunctionFor(this);};

    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).
     
    VK, Jun 18, 2005
    #4
  5. Elf M. Sternberg

    VK Guest

    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.activeElement (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).
     
    VK, Jun 18, 2005
    #5
  6. Elf M. Sternberg wrote:

    > One of the complaints about prototype.js (google for it if you're not
    > familiar with it) is that it's poorly documented.


    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.prototype.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
     
    Thomas 'PointedEars' Lahn, Jul 3, 2005
    #6
  7. commercial wrote:

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


    I did not state any of the kind.

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


    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.

    > [senseless reply including full top post quote snipped]



    PointedEars
     
    Thomas 'PointedEars' Lahn, Jul 7, 2005
    #7
  8. commercial wrote:

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


    I did not state any of the kind.

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


    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.

    > [senseless reply including full top post quote snipped]



    PointedEars
     
    Thomas 'PointedEars' Lahn, Jul 7, 2005
    #8
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Julie
    Replies:
    140
    Views:
    5,063
    George Neuner
    Jun 6, 2004
  2. Bart Torbert

    Cracking C structure

    Bart Torbert, Sep 8, 2004, in forum: C Programming
    Replies:
    2
    Views:
    577
    Ira Baxter
    Sep 10, 2004
  3. Carl Dau
    Replies:
    7
    Views:
    406
    John Brawley
    Jan 31, 2008
  4. TheRandomPast

    Cracking hashes with Python

    TheRandomPast, Nov 25, 2013, in forum: Python
    Replies:
    22
    Views:
    378
    Denis McMahon
    Nov 27, 2013
  5. Marc
    Replies:
    0
    Views:
    74
Loading...

Share This Page