9 Replies Latest reply on Mar 7, 2012 5:05 PM by John Hawkinson

    Javascript question about objects

    TᴀW Adobe Community Professional & MVP

      Hi,

       

      Let's say I've created a new object. One of the properties of my object

      is an array. I would like another property to return the length of that

      array. How do I do this.

       

      For instance here's some code (and I hope that the email interface

      doesn't screw this up so badly to make it unreadable. I'm going to avoid

      using square brackets as well by using round ones instead even though

      it's a syntax error, since square ones are known not to work with email.)

       

      function test(){

           this.a = new Array();

           this.b = this.a.length;

      }

       

      x = new test();

      x.a(0) = 3;

      x.a(1) = 3;

      x.b;

       

      Returns 0. In other words, just setting this.b = this.a.length doesn't

      not get updated automatically as this.a is added to.

       

      Now, I tried turning this.b into a method (ie this.b = function(){return

      this.a.length)  and that works, but then to get the length I keep having

      to refer to myObject.b().

       

      How can I avoid that? How can I have a built-in length property for an

      object.

       

      Hope this is clear and has come out legibly via email. Apologies in

      advance if it hasn't.

       

      Thanks,

      Ariel

       

        • 1. Re: Javascript question about objects
          absqua Level 4

          Until ExtendScript supports ECMAScript 5—does anyone know if this is going to happen, by the way?—which includes getter and setter properties, calling a function is the only way to do it.

           

          (Defining b() on the prototype, so that all the objects made from Test() will share the same function, is  the more efficient and accepted way to do it.)

           

          function Test(){
              this.a = [];
          }
          
          Test.prototype.b = function () {
              return this.a.length;    
          };
          
          var x = new Test();
          
          x.a[0] = 3;
          x.a[1] = 3;
          
          x.b();
          

           

          Jeff

          • 2. Re: Javascript question about objects
            TᴀW Adobe Community Professional & MVP

            Thanks Jeff.

             

            I don't understand what the difference is between hooking the function

            onto prototype vs. making it part of the constructor is? Can you elaborate?

             

            Ariel

            • 3. Re: Javascript question about objects
              absqua Level 4

              All objects created with new from Test() will share Test()'s prototype. So if you define your function there, all objects created from Test() will have access to the same function—by looking for it up the prototype chain—and it will only be defined once. If you define it in your constructor, each new object will have a separate copy of the function. It's a matter of efficiency, really. (And flexibility, I suppose: you could redefine the prototype's function later, and all objects that share it would then have access to the updated function.)

               

              There's a lot of information about this out there, though I'm having a hard time putting my hands on a good article at the moment.

              • 4. Re: Javascript question about objects
                TᴀW Adobe Community Professional & MVP

                I see what you're saying. I thought you were implying there was a

                difference in functionality which I couldn't understand. But there

                isn't, it's just a question of not wasting resources... Makes sense.

                 

                Thanks for the help.

                 

                Ariel

                • 5. Re: Javascript question about objects
                  [Jongware] Most Valuable Participant

                  Search for articles about "Javascript OOP". I must have read *all* of them (just last week!), until the penny dropped around http://nefariousdesigns.co.uk/object-oriented-javascript.html

                   

                  Here are some snippets of objects I've been using since, nested into oblivion but working nevertheless, all the way from top to bottom:

                   

                  // ... MUST be 'seen' before first used ...
                  // What a drag. Fortunately you can put all of them at the very
                  // top of your script.
                  obj_name.prototype = new obj_TableItem();
                  obj_head.prototype = new obj_TableItem();
                  ..
                  function obj_TableItem ()
                  {
                      this.name = 'default! Do Not Use!;
                  
                  
                      this.setValue = function (varn,value)
                      {
                          if (varn in this)
                          {
                              if (this[varn].hasOwnProperty("value"))
                                  this[varn].value = value;
                              else
                                  this[varn] = value;
                              return;
                          }
                          throw ("Table '"+this.name+"', element '"+varn+"' does not exist");
                      }
                      this.getValue = function (varn)
                      {
                          if (varn in this)
                          {
                              return this[varn];
                          }
                      //    throw ("Table '"+this.name+"', element '"+varn+"' does not exist");
                          return undefined;
                      }
                    this.toString = function ()
                    {
                      var r = [];
                      for (i in this)
                      {
                     if (this[i] && !(this[i] instanceof Function))
                     {
                       if (this[i] instanceof Array)
                      for each (j in this[i])
                        r.push ('name '+j.toString());
                       else
                       try{
                      r.push (i+'='+this[i].toString());
                       } catch (_) { r.push(i); }
                     }
                      }
                      return r.join("\r");
                    };
                  }
                  ..
                  function obj_name ()
                  {
                    this.name = 'name';
                  }
                  ..
                  function obj_head()
                  {
                    this.name = 'head';
                  }
                  ..
                  function obj_set ()
                  {
                     this.Tables = [ new obj_head(), new obj_name() ];
                    this.toString = function()
                    {
                      var i, r = [];
                      for (i=0; i<this.Tables.length; i++)
                     r.push (this.Tables[i].constructor.name+" > "+this.Tables[i].toString());
                      return r.join("\r");
                    }
                  };
                  

                   

                  and now I can use

                   

                  myObject = new obj_set();
                  alert (obj_set.toString());
                  

                   

                  The actual table objects contain sub-objects in turn, which *also* have 'toString' methods if I want to get something else than JS default's one.

                  I'm not showing the full script, by the way, as I just typed in line # 3657.

                  • 6. Re: Javascript question about objects
                    John Hawkinson Level 5

                    This all seems like a bad idea.

                    I would only add, Ariel, please name your constructors (functions intended to be called with the new operator) with a leading capital letter, i.e. function Test() not function test(). While Javascript gives you no apriori way to tell what is a Constructor and what is not, convention is to use an Initial capital, and it's a good and wise one. Anyone who later reads your code will thank you.

                     

                    I suppose it is lame, but my recommendation is to avoid the object-oriented inheritance most of the time, unless the task really calls for it. It just seems to serve to make code harder to read than conventional procedural paradigms.

                    • 7. Re: Javascript question about objects
                      Marc Autret Level 4

                      Hi all,

                       

                      1) About the original question—which doesn't regard prototype inheritance in itself: "How can I have a built-in length property for an object"—I think the most obvious approach is to directly use an Array as the base object. Since any Array is an Object you can append methods and properties to it while keeping benefit from the whole array's API:

                       

                       

                      var myData = [];
                      myData.myProp = 'foo';
                      myData.myMeth = function(){ return [this.myProp, this.length, this.join('|')]; };
                      
                      // Usage
                      // ---
                      myData[0] = 10;
                      myData[1] = 20;
                      alert( myData.myMeth() ); // => foo,2,10|20
                      // etc.
                      

                       

                      2) Now, if for whatever reason you cannot use the previous technique or need to deal with a precustomized Object structure such as:

                       

                      var myObj = {
                          items: [],
                          meth: function(){ alert("Hello World!"); },
                          // etc.
                          };
                      

                       

                      then the challenge is more difficult for the reasons that Jeff mentioned above. You can anyway mimic a kind of 'binding' mechanism this way:

                       

                       

                      // The original obj
                      // ---
                      var myObj = {
                          items: [],
                          meth: function(){ alert("Hello World!"); }
                          };
                      
                      // Adding length as a 'fake property' (getter and setter)
                      // ---
                      (myObj.length = function F(n)
                      {
                          if( !F.root )
                              {
                              (F.root=this).watch('length', function(_,ov,nv){ F(nv); return ov; });
                              F.valueOf = function(){ return F().valueOf(); };
                              F.toString = function(){ return F().toString(); };
                              }
                          if( 'undefined' == typeof n )
                              n = F.root.items.length >>> 0;
                          else
                              F.root.items.length = n;
                          return n;
                      }).call(myObj);
                      
                      // Usage
                      // ---
                      myObj.meth(); // => Hello World!
                      myObj.items[0] = 10;
                      myObj.items[1] = 20;
                      alert( myObj.length ); // => 2
                      
                      var x = myObj.length-1; // works too, thanks to F.valueOf
                      alert( x ); // => 1
                      
                      myObj.length = 5; // testing the setter
                      alert( myObj.length ); // => 5
                      alert( myObj.items ); // => 10,20,,,
                      

                       

                      Of course it's totally unreasonable to implement such an artillery just for the convenience of writing myObj.length instead of myObj.length()—and myObj.length=x instead of myObj.length(x)—so I only suggest this for the sake of experimentation.

                       

                      @+

                      Marc

                      • 8. Re: Javascript question about objects
                        [Jongware] Most Valuable Participant

                        John Hawkinson wrote:

                        I suppose it is lame, but my recommendation is to avoid the object-oriented inheritance most of the time, unless the task really calls for it. It just seems to serve to make code harder to read than conventional procedural paradigms.

                         

                        I specifically hunted down the How-To's of OOP in JS because I'm dealing with a large set of pluriform data tables, some of which have a generic, predictable format and some need extremely specific case-by-case handling with custom data types. And all of it should transparently pipe down to the main part of the script, 'cuz even with all those extra objects I'm not nearly done.

                        I went for the OOP approach because, frankly, I don't think it would be manageable without.

                         

                        Oh alright, I admit this is a far cry from my usual stuff.

                        • 9. Re: Javascript question about objects
                          John Hawkinson Level 5

                          Marc:

                                  (F.root=this).watch('length', function(_,ov,nv){ F(nv); return ov; });
                          

                          I had no idea that ExtendSCript had .watch() and .unwatch().

                          That's kind of weird. I guess I don't understand where Adobe's ExtendScript implementation comes from.

                           

                          Mozillla implies it's fairly nonstandard.

                           

                          Thanks for pointing that out!