Closure scope confusion

Discussion in 'Javascript' started by codethief, Mar 19, 2010.

  1. codethief

    codethief Guest

    Hello dear JavaScripters,

    I'm currently trying to get a script (http://bitbucket.org/codethief/
    pybsd/src/4890d9a4597a/static/js/jquery.inlineedit.js) working and
    have run into a problem which you JavaScript gurus surely can solve in
    no time unlike me (who certainly doesn't belong to this group, so
    please bear with me.)
    First of all, I'm doing an initial call, like so:

    #
    # window = WindowInteraction();
    # window.load_url('http://...');
    #

    .... which should open up a small window / css layer containing the
    website with the URL provided.

    (BTW: Interestingly, if I use the exact URL for window.load_url()
    under this script can be found, Google Chrome loops infinitely whereas
    Firefox doesn't. The latter is the wrong behavior since jQuery
    executes the JavaScript code in HTML data received over AJAX
    (according to the docs). Does Firefox somehow prevent execution of
    infinite loops or of JavaScript code received like this?)

    Now, let's get to my actual problem in line 161 (http://bitbucket.org/
    codethief/pybsd/src/4890d9a4597a/static/js/
    jquery.inlineedit.js#cl-161):
    The current version works so far which I rather achieved by guessing.
    Because:

    If I write
    # 'success': this.load_html
    or
    # 'success': load_html
    instead of the closure, it won't work at all ('this' is undefined in
    load_html, then) because, apparently, the context is lost and
    load_html is executed as stand-alone function. This is something I can
    follow.
    But, why doesn't the following run nicely?

    # 'success': function(data) {
    # this.load_html(data);
    # }

    .... results in: "this.load_html is not a function (line: 162)"

    Shouldn't 'this' be available in the closure or is it some kind of
    special variable? Why does a simply 'load_html(data)' (without 'this.'
    in front of it) even work? Are all properties of an object accessible
    like this (without 'this.' in front of them) from inside the prototype
    function?

    Thanks in advance for helping me understand this issue.
    codethief, Mar 19, 2010
    #1
    1. Advertising

  2. codethief

    Joe Nine Guest

    codethief wrote:
    > Hello dear JavaScripters,
    >
    > I'm currently trying to get a script (http://bitbucket.org/codethief/
    > pybsd/src/4890d9a4597a/static/js/jquery.inlineedit.js) working and
    > have run into a problem which you JavaScript gurus surely can solve in
    > no time unlike me (who certainly doesn't belong to this group, so
    > please bear with me.)
    > First of all, I'm doing an initial call, like so:
    >
    > #
    > # window = WindowInteraction();
    > # window.load_url('http://...');
    > #


    I think we can stop right there. Choose the name myWindow for example
    and you should get much further.
    Joe Nine, Mar 19, 2010
    #2
    1. Advertising

  3. codethief

    Thomas Allen Guest

    On Mar 19, 10:03 am, codethief <> wrote:
    > # 'success': function(data) {
    > # this.load_html(data);
    > # }
    >
    > ... results in: "this.load_html is not a function (line: 162)"
    >
    > Shouldn't 'this' be available in the closure or is it some kind of
    > special variable?


    Only if the 'success' function is being applied the execution context
    ("this") you expect it to be. See http://www.quirksmode.org/js/this.html
    for a decent introduction to "this," perhaps someone here knows of a
    better one.

    > Why does a simply 'load_html(data)' (without 'this.' in front of it)
    > even work?


    Because the following makes the load_html function available in the
    global scope:

    > # window = WindowInteraction();


    "window" being the host object.

    > Are all properties of an object accessible like this (without 'this.'
    > in front of them) from inside the prototype function?


    No, this worked by coincidence.

    Thomas
    Thomas Allen, Mar 19, 2010
    #3
  4. codethief

    codethief Guest

    I just realized that the 'new' operator is missing in front of
    'WindowInteraction()'. (However, it definitely worked without 'new' in
    the way I described above.)
    As 'load_html' (without 'this') didn't work, anymore, I figured out
    that 'this' may in fact be a special keyword:

    # var wi = this;
    #
    # jQuery.ajax({
    # 'url': url,
    # 'data': data,
    # 'dataType': 'html',
    # 'success': function(data) {
    # wi.load_html(data);
    # }
    # });

    .... works. So, is this is the only (and your recommended) solution?
    codethief, Mar 19, 2010
    #4
  5. codethief

    codethief Guest

    Oh sorry, I was writing such a long text at first (that I discarded)
    that I didn't notice the answers you posted in the meantime. This text
    also included some strange things that apparently were caused by me
    assigning something to 'window'.
    codethief, Mar 19, 2010
    #5
  6. codethief

    codethief Guest

    Now, that I have read the link you posted, Thomas, I think I
    understand the whole issue, now.

    Thanks to both of you!
    codethief, Mar 19, 2010
    #6
  7. codethief

    Thomas Allen Guest

    On Mar 19, 10:48 am, codethief <> wrote:
    > I just realized that the 'new' operator is missing in front of
    > 'WindowInteraction()'. (However, it definitely worked without 'new' in
    > the way I described above.)


    The main point, as noted by Joe, is that you shouldn't be trying to
    clobber the essential window variable as in your original code.

    > As 'load_html' (without 'this') didn't work, anymore, I figured out
    > that 'this' may in fact be a special keyword:


    Indeed. I'd recommend using an editor with better syntax highlighting
    if that wasn't already clear; a good one can make it obvious if you've
    accidentally employed a reserved keyword, etc. I recommend vim.

    > # var wi = this;
    > #
    > # jQuery.ajax({
    > #       'url': url,
    > #       'data': data,
    > #       'dataType': 'html',
    > #       'success': function(data) {
    > #               wi.load_html(data);
    > #       }
    > # });


    I think "var me = this", etc. is usually code smell. I see in the
    jQuery.ajax documentation that this function accepts a "context"
    property, so your code could be:

    jQuery.ajax({
    url: url,
    data: data,
    dataType: 'html',
    context: this,
    success: function(data) {
    this.load_html(data);
    }
    });

    Thomas
    Thomas Allen, Mar 19, 2010
    #7
  8. On Fri, 19 Mar 2010 at 07:18:08, in comp.lang.javascript, Thomas Allen
    wrote:

    <snip>
    >Only if the 'success' function is being applied the execution context
    >("this") you expect it to be. See http://www.quirksmode.org/js/this.html
    >for a decent introduction to "this," perhaps someone here knows of a
    >better one.

    <snip>

    That web page is seriously misleading.

    For instance
    "In JavaScript this always refers to the “owner†of the function
    we're executing,"...
    isn't true, and use of the word 'owner' is pretty dubious.

    For instance
    "In other words, we have to copy the function to our onclick
    property."
    will encourage people to think that functions can be copied by assigning
    them.

    John
    --
    John Harris
    John G Harris, Mar 19, 2010
    #8
  9. codethief

    Thomas Allen Guest

    On Mar 19, 4:11 pm, John G Harris <> wrote:
    > That web page is seriously misleading.


    Agreed, what's the recommended resource for this question? QuirksMode
    does have some decent stuff so I thought it'd be OK.

    Thomas
    Thomas Allen, Mar 19, 2010
    #9
  10. codethief

    David Mark Guest

    John G Harris wrote:
    > On Fri, 19 Mar 2010 at 07:18:08, in comp.lang.javascript, Thomas Allen
    > wrote:
    >
    > <snip>
    >> Only if the 'success' function is being applied the execution context
    >> ("this") you expect it to be. See http://www.quirksmode.org/js/this.html
    >> for a decent introduction to "this," perhaps someone here knows of a
    >> better one.

    > <snip>
    >
    > That web page is seriously misleading.
    >
    > For instance
    > "In JavaScript this always refers to the “owner†of the function
    > we're executing,"...
    > isn't true, and use of the word 'owner' is pretty dubious.
    >
    > For instance
    > "In other words, we have to copy the function to our onclick
    > property."
    > will encourage people to think that functions can be copied by assigning
    > them.
    >


    Yeah, that's vintage PPK. Unfortunately, browser scripting history (as
    written on the Web) is full of such follies. I think he wrote a book
    about it too. It's so hard getting people to unlearn so much bogus
    information.
    David Mark, Mar 19, 2010
    #10
  11. codethief

    David Mark Guest

    Thomas Allen wrote:
    > On Mar 19, 4:11 pm, John G Harris <> wrote:
    >> That web page is seriously misleading.

    >
    > Agreed, what's the recommended resource for this question? QuirksMode
    > does have some decent stuff so I thought it'd be OK.
    >


    You've got to be careful with that site. It is very much a part of the
    cult of observational programming (sort of a shrine actually). In other
    words, they see things in browsers and then try to guess what they mean.
    Often these faulty inferences are contradicted later, leading to
    complaints about "buggy browsers". ;)
    David Mark, Mar 19, 2010
    #11
  12. Thomas Allen wrote:

    > codethief wrote:
    >> Why does a simply 'load_html(data)' (without 'this.' in front of it)
    >> even work?

    >
    > Because the following makes the load_html function available in the
    > global scope:
    >
    >> # window = WindowInteraction();

    >
    > "window" being the host object.


    I am afraid you are seriously mistaken.

    First of all, _a_ host object. Secondly, `window' would not be an object,
    it would be the name of a property storing a reference to an object.
    Thirdly, if `window' would resolve to the host-defined property of the
    Global Object that refers to a Window instance, it is very likely that this
    property would be read-only and the assignment had either no effect or
    would cause a runtime error. Fourthly: If the assignment was successful,
    the stored reference value would be replaced, and the property in question
    would not necessarily refer to a host object anymore; it would store
    whatever WindowInteraction() returned. Those are a great many ifs.

    Another possible explanation that complies better with Occam's Razor is
    that `window' does _not_ refer to a host object here, and that the value of
    a variable or user-defined property has been overwritten.

    What happens here: WindowInteraction() is called with its `this' value
    being set to the Global Object (as evaluation of `WindowInteraction'
    results in Reference(base=undefined, name=WindowInteraction, ...) because
    the only production `MemberExpression : Identifier' can be applied), and it
    contains the following statement:

    this.load_html = function(html) {
    // ...
    };

    As a result, the Global Object is augmented with a property named
    `load_html' which stores a reference to a function object. So load_html()
    can be called later without `this.' being part of the expression, either
    because the Global Object is in the scope chain of any execution context,
    or, less likely, because there is another callable `load_html' in the scope
    chain.

    After the call, an assignment to `window' is tried. Since no other object
    in the scope chain has a property with that name, an attempt takes place to
    overwrite the `window' property of the Global Object. This attempt might
    even be successful. However, tests with both Iceweasel 3.5.8 (Gecko
    1.9.1.8) and IE/MSHTML 6.0.2800.1106 suggest that this assignment is likely
    to fail: Gecko issues a warning saying "window is read-only" without
    changing the value, and MSHTML throws ReferenceError("Illegal assignment").

    As is obvious now, this call is a rather bad move. However, the OP has
    found out by now that the `new' was missing, which changes everything:
    `this' would then no longer refer to the Global Object, but to the object
    that is about to be constructed. As a result, the unqualified call
    `load_html()' would fail then (except if there was another callable
    load_html in the scope chain).


    HTH

    PointedEars
    --
    realism: HTML 4.01 Strict
    evangelism: XHTML 1.0 Strict
    madness: XHTML 1.1 as application/xhtml+xml
    -- Bjoern Hoehrmann
    Thomas 'PointedEars' Lahn, Mar 19, 2010
    #12
  13. codethief wrote:

    > I just realized that the 'new' operator is missing in front of
    > 'WindowInteraction()'. (However, it definitely worked without 'new' in
    > the way I described above.)


    As I explained.

    > As 'load_html' (without 'this') didn't work, anymore, I figured out
    > that 'this' may in fact be a special keyword:


    It is, but differently than you think.

    > # var wi = this;
    > #
    > # jQuery.ajax({
    > # 'url': url,
    > # 'data': data,
    > # 'dataType': 'html',
    > # 'success': function(data) {
    > # wi.load_html(data);
    > # }
    > # });
    >
    > ... works. So, is this is the only (and your recommended) solution?


    No, you can use a more direct closure:

    jQuery.ajax({
    'url': url,
    'data': data,
    'dataType': 'html',
    'success': (function (x) {
    return function() {
    x.load_html(data);
    };
    }(wi))
    });

    As for recommended: Avoid jQuery to begin with.

    Secondly, you need to understand that value of `this' is determined *at*
    *runtime*; it is determined by the way the method is called, not by the way
    it is defined.

    Next time, do not prepend code lines with anything but white-space. Posted
    code should be executable as-is.


    PointedEars
    --
    Anyone who slaps a 'this page is best viewed with Browser X' label on
    a Web page appears to be yearning for the bad old days, before the Web,
    when you had very little chance of reading a document written on another
    computer, another word processor, or another network. -- Tim Berners-Lee
    Thomas 'PointedEars' Lahn, Mar 19, 2010
    #13
  14. codethief wrote:

    > Now, that I have read the link you posted, Thomas, I think I
    > understand the whole issue, now.


    I seriously doubt it, given that I find the referred explanation strewn with
    gaping defects in correctness.

    > Thanks to both of you!


    Both of who?


    PointedEars
    --
    var bugRiddenCrashPronePieceOfJunk = (
    navigator.userAgent.indexOf('MSIE 5') != -1
    && navigator.userAgent.indexOf('Mac') != -1
    ) // Plone, register_function.js:16
    Thomas 'PointedEars' Lahn, Mar 19, 2010
    #14
  15. codethief

    Thomas Allen Guest

    On Mar 19, 6:04 pm, Thomas 'PointedEars' Lahn <>
    wrote:
    > Both of who?


    The only two respondents at that point, presumably.

    Thomas
    Thomas Allen, Mar 19, 2010
    #15
    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. Steven W. Orr

    Another dumb scope question for a closure.

    Steven W. Orr, Jan 9, 2008, in forum: Python
    Replies:
    2
    Views:
    240
    Waldemar Osuch
    Jan 9, 2008
  2. grocery_stocker
    Replies:
    5
    Views:
    101
    Robert Dober
    Aug 31, 2007
  3. Julian Mehnle
    Replies:
    0
    Views:
    238
    Julian Mehnle
    Jul 17, 2003
  4. Kidogg

    Scope (closure?) question

    Kidogg, May 25, 2007, in forum: Javascript
    Replies:
    2
    Views:
    91
    Kieran
    May 27, 2007
  5. Andrey Fedorov
    Replies:
    2
    Views:
    93
    Andrey Fedorov
    Oct 27, 2008
Loading...

Share This Page