5 Replies Latest reply on May 9, 2011 10:07 PM by John Hawkinson

    variable scope and navigating a ScriputUI interface

    Jimbo Jackson Level 1

      I'm having a little trouble understanding how to reference variables outside of their scope. If my understanding of JS is correct, encapsulation per say doesn't formally exist in JS, but in regards to variables one can keep a variable local to the curly brackets it's declared within by declaring it with the keyword, "var". I like that aspect of the language, but how does one go about referencing a variable with local scope, globally? For example, in the code below I can reference the selected value of the dropdownlist control, myDdlist, with an eventListenter and the "this" reference. But, let's say I have a lot of dropdownlists on different tabs, and in different groups, and whose values I need to capture at the click of a single button; how does one get the value of a UI control such as a dropdownlist that is burried deep in a complex dialog? For example, how would one get the value of myDdlist and myDdlist2 when myRunBtn is clicked?

       

      Thanks!

       

       

       

      function myFlopperStopper()

      {

           var dlg = new Window('dialog','FlopperStopper');

       

           this.createDialog = function()

           {

                var myPnl = dlg.add('panel',undefined,'FlopperStopperSettings');

                var myGroup = myPnl.add('group');

                var myInnerGroup = myGroup.add('group');

                var myDdlist = myInnerGroup.add('dropdownlist',undefined,['Wing Whacker 1','Wing Whacker 2', 'Wing Whacker 3']);

                myDdlist.addEventListener('change',getSelection);

                var subGrp = myInnerGrp.add('group');

                var myPnl2 = subGrp.add('panel',undefined,'My Setting');

                var myDdlist2 = myPnl2.add('dropdownlist',undefined,['choice 1', 'choice 2', 'choice 3']);

       

                var myRunBtn = myInnerGrp.add('button',undefined,'Run');

                myRunBtn.addEventListener('click',getControlValues);

       

       

           }

       

       

           var getSelection = function()

           {

                var userSelection = this.selection;

           }

       

           var getControlValues = function()

           {

              ???????

           }

      }

        • 1. Re: variable scope and navigating a ScriputUI interface
          John Hawkinson Level 5
          I'm having a little trouble understanding how to reference variables outside of their scope.

          Javascript has lexical scoping. But the general answer is, "do not reference variables outside their scope!"

          in regards to variables one can keep a variable local to the curly brackets it's declared within by declaring it with the keyword, "var".

           

          Almost, but not quite. Javascript does not have block scope -- it has function scope. Despite appearances. Any variable that is declared in curly brackets has its variable declaration "hoisted" to the top of the function where it is declared. So for instance:

           

          function a(i) {
            if (i===1) { var a = 7; }
            if (i!==1) { var a = 6; }
            return a;
          }
          

           

          That will return either 6 or 7, depending on the value of i. As a result of this language misfeature, you should never write code like this.

           

          Always declare your variables at the top of the function (where they will be hoisted to).

          Also, always validate you code with JSLint to catch such problems. So write the above as:

           

          function a(i) {
            var a;
            if (i===1) { a = 7; }
            if (i!==1) { a = 6; }
            return a;
          }
          

           

          (ok, sure it could have been written much more succinctly).

          I like that aspect of the language, but how does one go about referencing a variable with local scope, globally? For example, in the code below I can reference the selected value of the dropdownlist control, myDdlist, with an eventListenter and the "this" reference.

           

          Your use of this is just plain incorrect:

           

          this.createDialog = function()
          …
          var userSelection = this.selection;
          

           

          In general, I would recommend avoiding this. Unless you use the new operator, this is bound to the global object. So those two statements above are equivalent to:

           

          createDialog = function()
          ...
          var userSelction = selection;
          

           

          Except that they are massivingly confusing.

           

          Anyhow:

           

          But, let's say I have a lot of dropdownlists on different tabs, and in different groups, and whose values I need to capture at the click of a single button; how does one get the value of a UI control such as a dropdownlist that is burried deep in a complex dialog? For example, how would one get the value of myDdlist and myDdlist2 when myRunBtn is clicked?

           

          It's easy. Just declare your event handler that for myRunBtn in the same lexical scope as myDdlist and myDdlist2. For instance, bring getControlValues into createDialog().

           

          (Incidently, I'm puzzled why you are using "function myFlopperStopper()" in some places and "var createDialog = function() {" in others. Most people just write the former. The latter does make it a bit more obvious what is going on under the hood, at the cost of a somewhat unfamiliar paradigm. But it's peculiar to use both. I would pick one and stick with it.)

           

          E.g.:

          function myFlopperStopper() {
            function createDialog() {
              var myDdlist;
          
              function getControlValues() {
                $.writeln("This is myDdlist "+myDdlist);
              }
          
              myDdlist = /* whatever */;
              var myRunBtn = /*whatever else */;
              myRunBtn.addEventListener('click', getControlValues);
            }
          }
          

           

          Of course, it's a bit weird that you never call createDialog(). Maybe you're treating myFlopperStopper like a class and you're going to instantiate it with new later? I wouldn't really recommend doing it that way, but you certainly can.

          • 2. Re: variable scope and navigating a ScriputUI interface
            John Hawkinson Level 5

            Of course, it's a bit weird that you never call createDialog(). Maybe you're treating myFlopperStopper like a class and you're going to instantiate it with new later? I wouldn't really recommend doing it that way, but you certainly can

            Err...except that you'll have no way to call createDialog once your myFlopperStopper is created. You won't be able to call myFlopperStopper.createDialog() or anything like that (there are a variety of patterns you could use that would permit that, including assigning the function with "myFlopperStopper.createDialog = ..."  or changing myFlopperStopper to be something like:

             

            myFlopperStopper = (function() {
              ...
              return {
                createDialog: function() { ... }
              }
            }());
            

             

            where myFlopperStopper is assigned the result of calling an anonymous function that returns a JavaScript object with a property called createDialog.

             

            But all of this is confusing, I would suggest you avoid it and not worry about class-based inheritance unless you really need to create a lot of plural objects.

             

            It might be clearer if you gave us a full example of what you wanted to do.

            • 3. Re: variable scope and navigating a ScriputUI interface
              Jimbo Jackson Level 1

              John, thanks for your analysis and help. My apologies for the very late reply. The example I wrote in this thread is just a quick, incomplete, over-simplified improvisation. You answered the main question I had, though ....

               

              function(){return A.apply(null,[this].concat($A(arguments)))}

              function(){return A.apply(null,[this].concat($A(arguments)))}It's easy. Just declare your event handler that for myRunBtn in the same lexical scope as myDdlist and myDdlist2. For instance, bring getControlValues into createDialog().

               

               

              I structured my script based on a script by Tom Ruark that is included in the CS5 sample scripts.

              function(){return A.apply(null,[this].concat($A(arguments)))}

              function(){return A.apply(null,[this].concat($A(arguments)))}(Incidently, I'm puzzled why you are using "function myFlopperStopper()" in some places and "var createDialog = function() {" in others. Most people just write the former. The latter does make it a bit more obvious what is going on under the hood, at the cost of a somewhat unfamiliar paradigm. But it's peculiar to use both. I would pick one and stick with it.)

               

               

              I do use the new operator in my script to instantiate a myFlopperStopper object, so this would be bound to the calling object that is instatiated using the new, correct?

              function(){return A.apply(null,[this].concat($A(arguments)))}

              function(){return A.apply(null,[this].concat($A(arguments)))}In general, I would recommend avoiding this. Unless you use the new operator, this is bound to the global object. So those two statements above are equivalent to ....

               

               

              The structure of my script is based on a Tom Ruark script that is included in the CS5 samples ... and I was comfortable using this structure becuase it is familiar to the OOD that I'm somewhat accustomed to.

              function(){return A.apply(null,[this].concat($A(arguments)))}

              function(){return A.apply(null,[this].concat($A(arguments)))}(Incidently, I'm puzzled why you are using "function myFlopperStopper()" in some places and "var createDialog = function() {" in others. Most people just write the former. The latter does make it a bit more obvious what is going on under the hood, at the cost of a somewhat unfamiliar paradigm. But it's peculiar to use both. I would pick one and stick with it.)

               

               

              When I wrote this thread, I was pressed for time  ... this more resembles how my actual script is structured.

               

              function(){return A.apply(null,[this].concat($A(arguments)))}

              function(){return A.apply(null,[this].concat($A(arguments)))}

              try

              {

                   var fs = new myFlopperStopper();    

                   var fsDialog = fs.createDialog();

                   fsDialog.show();

              }

              catch (e)

              {

                 // inform user of error and cancel script.

              }

               

               

              //------------------------------------

              // myFlopperStopper

              //------------------------------------

               

              function myFlopperStopper()

              {

                   var dlg = new Window('dialog','FlopperStopper');

               

                   this.createDialog = function()

                   {

                        var myPnl = dlg.add('panel',undefined,'FlopperStopperSettings');

                        var myGroup = myPnl.add('group');

                        var myInnerGroup = myGroup.add('group');

                        var myDdlist = myInnerGroup.add('dropdownlist',undefined,['Wing Whacker 1','Wing Whacker 2', 'Wing Whacker 3']);

                        myDdlist.addEventListener('change',getSelection);

                        var subGrp = myInnerGrp.add('group');

                        var myPnl2 = subGrp.add('panel',undefined,'My Setting');

                        var myDdlist2 = myPnl2.add('dropdownlist',undefined,['choice 1', 'choice 2', 'choice 3']);

               

                        var myRunBtn = myInnerGrp.add('button',undefined,'Run');

                        myRunBtn.addEventListener('click',getControlValues);

               

               

                   }

               

               

                   var getSelection = function()

                   {

                        var userSelection = this.selection;

                   }

               

                   var getControlValues = function()

                   {

                      ???????

                   }

              }

               

               

               

              • 4. Re: variable scope and navigating a ScriputUI interface
                Jimbo Jackson Level 1

                The loosely typed nature of JS is what's throwing me off, btw. It's a fun language, though, and I'm thoroughly enjoying the learning curve. 

                • 5. Re: variable scope and navigating a ScriputUI interface
                  John Hawkinson Level 5

                  Incidently, you are missing semicolons at the ends of your functions. If you define a function with a function expression, it needs a terminal semicolon. Javascript does let you omit semicolons all over the place, but it's a good way to be sad later. So use:

                   

                  var getSelection = function() {
                    var userSelection = this.selection;
                  };
                  

                   

                  Anyhow.

                   

                  I would definitely recommend avoiding 'new' like the plague, especially if you have no requirement for class-based inheritance. If you need inheritance, you can use prototypal inheritance, but most people don't really need inheritance at all for simple Javascript.

                   

                  Yes, if you're using new then I believe all of your thises are fine.