JavaScript Variable Scoping and Execution Contexts

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Greg Swindle

    JavaScript Variable Scoping and Execution Contexts

    Hello,
    I have a question about how prototyping relates to variables and their
    scope.

    Given the following code:
    ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
    var ParentObject = function()
    {
    var objectName = "ParentObje ct";

    this.getObjectN ame = function()
    {
    return objectName;
    }
    this.setObjectN ame = function( newObjectName )
    {
    objectName = newObjectName;
    }
    }

    var ChildObject = function()
    {
    ParentObject.ap ply(this, arguments);

    this.tryToRefer enceObjectNameV ar = function()
    {
    var text = null;
    // Attempt to reference the variable "objectName ",
    // which was created in ParentObject.
    try
    {
    text = "ChildObjec t's \"objectName \"" +
    " variable is \"" + objectName + "\"";
    }
    catch( error )
    {
    alert( "Error: " + error.name +
    " at " + "line: " + error.lineNumbe r + "\n" +
    "\"" + error.message + "\"\n" +
    "Stack:\n" + error.stack + "\n" );
    }
    return text;
    }
    }

    var child = new ChildObject();
    child.tryToRefe renceObjectName Var();
    ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
    The last call throws this ReferenceError:

    Error: objectName is not defined

    Is this because prototyping, which I have done by calling the "apply"
    method of a constructor, creates an independent execution context
    that cannot be accessed from the "child" object's execution context? In
    other words,
    when I call

    child.getObject Name();
    child.tryToRefe renceObjectName Var();

    are two (or several) different execution contexts being referenced? How is
    the
    JavaScript interpreter handling this?

    TIA,
    Greg


  • Michael Winter

    #2
    Re: JavaScript Variable Scoping and Execution Contexts

    On Sat, 4 Sep 2004 12:00:09 -0500, Greg Swindle <greg@swindle.n et> wrote:

    [snip]
    [color=blue]
    > var ChildObject = function()
    > {
    > ParentObject.ap ply(this, arguments);[/color]

    I must say that I'm surprised this approach works. However, a quick read
    of ECMA-262 suggests it should but I wouldn't use it. The apply method is
    relatively recent addition to the language. The prototype object is widely
    supported and will yield the same net effect

    [snip]
    [color=blue]
    > The last call throws this ReferenceError:
    >
    > Error: objectName is not defined
    >
    > Is this because prototyping, which I have done by calling the "apply"
    > method of a constructor,[/color]

    A function is only really a constructor when it's called using the new
    operator, which invokes the [[Construct]] internal method. What you've
    effectively done is pass object, child, to a function which has tacked on
    two properties: getObjectName and setObjectName. There isn't really any
    inheritance here.
    [color=blue]
    > creates an independent execution context that cannot be accessed from
    > the "child" object's execution context?[/color]

    When a function is called, a new execution context is created. The local
    variables, inner functions and formal parameters are added to the
    activation object for that function.

    When an inner function is defined, it's [[Scope]] property (what it uses
    to look-up identifiers) is set to the activation object of the function
    which defines it. So, in ParentObject, the [[Scope]] property of
    getObjectName and setObjectName is set to the activation object of the
    ParentObject function. This allows them to reference the local variable
    objectName.

    However, as I said above, all you're doing when you call apply in your
    code is add extra properties to the ChildObject object. There is no
    modification of the scope chain, and so you can't access the local
    variable. In fact, you will never be able to access it without inner
    functions. You'll either have to place objectName in a common context or
    use accessor functions.

    What exactly are you trying to accomplish? There might be some other
    possibilities.

    For more information on closures, see the FAQ notes:

    <URL:http://www.jibbering.c om/faq/faq_notes/closures.html>

    Hope that helps,
    Mike

    --
    Michael Winter
    Replace ".invalid" with ".uk" to reply by e-mail.

    Comment

    • Greg Swindle

      #3
      Re: JavaScript Variable Scoping and Execution Contexts

      Mike,

      Yes, that helps, thanks. After I wrote this email I found the very article
      you cite, and it was quite enlightening.

      Some comments:
      [color=blue]
      > On Sat, 4 Sep 2004 12:00:09 -0500, Greg Swindle <greg@swindle.n et> wrote:
      >
      > [snip]
      >[color=green]
      > > var ChildObject = function()
      > > {
      > > ParentObject.ap ply(this, arguments);[/color]
      >
      > I must say that I'm surprised this approach works. However, a quick read
      > of ECMA-262 suggests it should but I wouldn't use it. The apply method is
      > relatively recent addition to the language. The prototype object is widely
      > supported and will yield the same net effect[/color]

      I "discovered " this when I was playing around with the Decorator design
      pattern and wanted a way to
      simulate calling a parent class's constructor function (e.g., like calling
      "super" in Java). Because this was a while back,
      and because I did not yet know about either "apply" or "call," I prototyped
      like this:

      function ChildObject( someArg )
      {
      this.temp = ParentObject;
      this.temp(someA rg );
      delete this.temp;
      }

      In short, I wanted to not only prototype, but also call the function being
      prototyped. For a full example,
      please see the source code here:



      [snip]
      [color=blue]
      > A function is only really a constructor when it's called using the new
      > operator, which invokes the [[Construct]] internal method. What you've
      > effectively done is pass object, child, to a function which has tacked on
      > two properties: getObjectName and setObjectName. There isn't really any
      > inheritance here[/color]

      Strictly speaking, is there *ever* inheritance *anywhere* in JavaScript?

      [snip]
      [color=blue]
      > What exactly are you trying to accomplish? There might be some other
      > possibilities.[/color]

      Good question. I'm trying to write JavaScript in a way that makes it
      resemble more familiar OO
      code (e.g., Java, C++, C#). This is why I like using function literals.
      Writing functions
      as literals has several advantages to me:

      1. Assigning a function literal to a variable makes the variable "appear"
      more like an object.
      2. I can immediately "tack on" properties and methods to it before any
      internal operations are
      called, thereby simulating multiple inheritance/interface implementation.
      3. I can embed functions within my object, which simulates privately scoped
      functions in class-
      based OO languages. With functions nestled inside my function's curly
      braces, it "looks" clearer that these
      functions are only intended to support the overall "object."
      4. It allows me to simulate privately scoped variables, which effects a
      stronger form of data
      encapsulation. Tangentially, that's one of the few things that really irks
      me about Python: all class
      variables are public, which frankly sucks.
      5. Finally, it directly exposes one of JavaScript's most powerful features:
      functions are also data.
      Therefore, the following code:

      var ChildObject = function( childName, childAge )
      {

      }

      makes more sense to me when I construct ChildObject with the "new" keyword,
      as opposed
      to constructing a function.

      Thanks for wading through the verbosity. I welcome your comments.

      Regards,
      Greg


      Comment

      • Michael Winter

        #4
        Re: JavaScript Variable Scoping and Execution Contexts

        On Sat, 4 Sep 2004 21:04:48 -0500, Greg Swindle <greg@swindle.n et> wrote:

        [snip]
        [color=blue]
        > http://www.swindle.net/Content/Greg/...sDecorator.htm[/color]

        Sorry to nit-pick...

        HTML pages should always contain a document type declaration. When
        present, it usually forces the browser to render the page according to
        standards, resulting in a much more consistent presentation across user
        agents. All new pages should be written using a combination of Strict HTML
        and CSS, with Transitional HTML reserved for legacy pages.

        Valid HTML requires that SCRIPT elements include the type attribute. The
        language attribute has been deprecated in favour of type and should not be
        used any more. Similarly, script hiding through the use of SGML comment
        delimiters is an anachronism.

        /* c - Currency symbol to use (defaults to none).
        * g - Grouping symbol (defaults to none).
        */
        Number.prototyp e.toCurrency = function(c, g) {
        c = c || ''; g = g || '';
        var p = (0 > this) ? '-' + c : c,
        m = new String(Math.rou nd(Math.abs(thi s))),
        i = '', f, j, dP = 2, gS = 3;

        while(m.length < 1 + dP) {m = '0' + m;}
        f = m.substring((j = m.length - dP));
        if(g) {
        while(j > gS) {i = g + m.substring(j - gS, j) + i; j -= gS;}
        }
        return p + m.substring(0, j) + i + '.' + f;
        }

        The code above is less likely to introduce errors as no multiplication or
        division is required. This means that the number should represent pennies
        not larger denominations. However, when it comes to financial
        calculations, it always best to use integer arithmetic.

        [snip]
        [color=blue]
        > Strictly speaking, is there *ever* inheritance *anywhere* in JavaScript?[/color]

        Yes. You use it in the URL you presented.

        You can add an object as the prototype for a constructor function. When
        you instantiate an object from that constructor, it will inherit the
        properties of its prototype. You should also use the prototype object to
        add members that do not need direct access via a closure to the formal
        arguments or local variables of the constructor (that is, when they do not
        need to "privaledge d").

        function Parent_firstMet hod() {
        }
        function Parent() {
        // Uses an argument or local variable:
        this.secondMeth od = function() {};
        }
        Parent.prototyp e.firstMethod = Parent_firstMet hod;
        Parent.prototyp e.thirdMethod = function() {};

        function Child() {
        }
        Child.prototype = new Parent();

        var myChild = new Child();
        myChild.firstMe thod();
        myChild.secondM ethod();
        myChild.thirdMe thod();

        Unfortunatly, this approach doesn't allow you to pass arguments at will to
        the parent; you can only pass an argument on the Child.prototype line
        above.

        What you've been doing is a modified form of object augmentation: take an
        existing object and add other members to it. This is perfectly viable and
        you're not the first to use it, but it's not really inheritance.

        You might want to take a look at Douglas Crockford's web site. He has some
        interesting articles on OO Javascript.

        <URL:http://www.crockford.c om/>

        [snip]
        [color=blue]
        > I'm trying to write JavaScript in a way that makes it resemble more
        > familiar OO code (e.g., Java, C++, C#). This is why I like using
        > function literals.[/color]

        Do you mean function expressions?
        [color=blue]
        > Writing functions as literals has several advantages to me:[/color]

        [snip]

        The ability to encapsulate related functions and hide data is extremely
        useful.
        [color=blue]
        > var ChildObject = function( childName, childAge )
        > {
        > }
        >
        > makes more sense to me when I construct ChildObject with the "new"
        > keyword, as opposed to constructing a function.[/color]

        Each to their own. :)

        I use the Java naming scheme: start classes (in this case, constructor
        functions) with capitals and normal functions with lowercase. That's
        enough for me.

        Mike

        --
        Michael Winter
        Replace ".invalid" with ".uk" to reply by e-mail.

        Comment

        • Lasse Reichstein Nielsen

          #5
          Re: JavaScript Variable Scoping and Execution Contexts

          "Greg Swindle" <greg@swindle.n et> writes:
          [color=blue]
          > Strictly speaking, is there *ever* inheritance *anywhere* in JavaScript?[/color]

          JavaScript (or really ECMAScript) is a prototype based object oriented
          language. That means that there are no classes, so inheritance cannot
          be between classes as in class based languages. Instead, prototype
          based inheritance is between objects.

          Prototype based inheritance is implemented using a "protoype
          chain". When you look up a property of an object, the language first
          checks whether the object has that property on itself. If it hasn't,
          it starts traversing the prototype chain and checking for the property
          on the prototypes. Almost all objects has a non-empty prototype chain
          with the (original) value of Object.prototyp e at the end (the
          exception is Object.prototyp e itself, which has an empty prototype
          chain).

          Every time you create an object using a constructor function, call it
          Foo, the value of Foo.prototype is used as the prototype of the new
          object.

          Example:
          ---
          function Foo(){} // constructor function with no initialization
          var object = new Object(); // new object
          object.bar = 42; // with properties bar
          object.baz = "hello world";// and baz
          Foo.prototype = object; // set as prototype for objects created by Foo
          var foo = new Foo(); // create new object from Foo
          foo.bar = 37; // set property bar of foo.
          alert([foo.bar, foo.baz]); // alerts: 37, hello world

          // The link to the prototype object is live,
          // it's not just the properties that are inherited
          // but the entire object:
          object.bif = true; // change prototype object property after creation
          alert(foo.bif); // alerts: true
          ---
          [color=blue]
          > Good question. I'm trying to write JavaScript in a way that makes it
          > resemble more familiar OO code (e.g., Java, C++, C#).[/color]

          You are in for some hard work, because the difference between a
          prototype based language and a class based one is at the fundamental
          level. There are no classes in Javascript.

          /L
          --
          Lasse Reichstein Nielsen - lrn@hotpop.com
          DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleD OM.html>
          'Faith without judgement merely degrades the spirit divine.'

          Comment

          • Thomas 'PointedEars' Lahn

            #6
            Re: JavaScript Variable Scoping and Execution Contexts

            Lasse Reichstein Nielsen wrote:
            [color=blue]
            > "Greg Swindle" <greg@swindle.n et> writes:[color=green]
            >> Good question. I'm trying to write JavaScript in a way that makes it
            >> resemble more familiar OO code (e.g., Java, C++, C#).[/color]
            >
            > You are in for some hard work, because the difference between a
            > prototype based language and a class based one is at the fundamental
            > level. There are no classes in Javascript.[/color]
            ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^
            prior to version 2, or prior to ECMAScript 4 implementations like JScript 6.


            Pointed"JFTR"Ea rs
            --
            .... the gentle sound of crashing software ...

            Comment

            Working...