www.webdeveloper.com
Results 1 to 15 of 19

Thread: Best way to create a reusable object/class

Hybrid View

  1. #1
    Join Date
    Jun 2005
    Posts
    11

    Best way to create a reusable object/class

    Hey,
    I'm new to OOP in javascript.
    What I have been doing so far to make a "class"/Reusable object is this:

    PHP Code:
    function Dog(){
        
    this.name name;
        
        
    this.bark = function(){
            
    // some code
        
    }

    However, recently I have been reading a book on Javascript and it said that the problem with this way of doing it, is that the bark() function will be loaded into memory for each Dog object created this way, while the body of that function is the same for each object. There was an explanation on how it could be done better but I did not understand it fully.

    Let's say I want:
    - A Person class with a name property and a jump() method.
    - An animal class with a isMammal property and move() method.
    - A Dog class with a numberOfLegs property and a bark() method which is a subclass of Animal (thus inherits the isMammal property and move() method.
    - Each of those classes in its own file.

    What would these files look like when I do it the right way?

    This book I've been reading is actually pretty good, but at some points it's explanation is not rich enough.

    It explains that functions are objects and that objects in javascript are just key-value pairs. It says that each function has a prototype property which points to a blank Object(), but you can make it point to something else? or maybe I misunderstood it. I still don't understand how it really works. Maybe someone can give me a good link or explain me how the whole prototype thing works.

    Hope you can help me out, it would really help me to start understanding Javascript better in its entirety. Because most things I can get working now, but I want to know why it works and whether I'm doing it in an efficient way.

    Thanks in advance,
    Stefan

  2. #2
    Join Date
    Dec 2003
    Location
    Bucharest, ROMANIA
    Posts
    15,428
    JavaScript has no classes. It works only with objects. Objects can be created in several ways. mainly:

    literally (using JSON)
    Code:
    var myObject={
    property:'somevalue',
    method:function(){alert(this.property)}
    }
    // or
    var myObject={};
    myObject.property='somevalue';
    myObject.method=function(){alert(this.property)};
    using a constructor:
    Code:
    function myConstructor(value){
    this.property=value;
    this.method=function(){alert(this.property)}
    }
    var myObject=new myConstructor('somevalue');
    Now, if I well understood, you need to create dynamically different methods, for different objects, but using the same constructor. Is this what you want?:
    Code:
    function Person(name,method){
    this.name=name;
    this[method]=function(){alert(this.name)}
    }
    var John=new Person('John','shout');
    var Alice=new Person('Alice','say');
    John.shout();
    Alice.say();

  3. #3
    Join Date
    Jul 2003
    Location
    The City of Roses
    Posts
    2,503
    Quote Originally Posted by stefan2 View Post
    PHP Code:
    function Dog(){
        
    this.name name;
        
        
    this.bark = function(){
            
    // some code
        
    }

    ... the problem with this way of doing it, is that the bark() function will be loaded into memory for each Dog object created ... What would these files look like when I do it the right way?
    It would look like this:

    PHP Code:
    function Dog(name) {
        
    this.name name;
    }

    Dog.prototype.bark = function () {
        
    // some code
    }; 
    The "prototype" property is automatically created with each function, and when you do new Dog(), the new object will inherit from Dog.prototype. So every instance of Dog will inherit and share the same copy of the function "bark".

    Quote Originally Posted by stefan2 View Post
    ... A Dog class ... which is a subclass of Animal ...
    Inheritance in JavaScript can be an in-depth topic, because there are several techniques. But the most basic looks like this:

    PHP Code:
    function Animal() {
    }

    Animal.prototype.move = function () {
        
    // some code
    };

    function 
    Dog(name) {
        
    this.name name;
    }

    Dog.prototype = new Animal();

    Dog.prototype.bark = function () {
        
    // some code
    }; 
    We start with a basic "Animal" function, and we attach a "move" method to Animal's prototype, so every instance of Animal will inherit the move method. Then for "Dog", we replace the prototype object with an instance of Animal. That means every instance of Dog will also inherit the move method, because Dog instances inherit from Dog.prototype, and now Dog.prototype inherits from Animal.prototype.

    If you want to learn from two of the better JavaScript books, I'd recommend Object-Oriented JavaScript first, then JavaScript Patterns for more advanced stuff.
    for(split(//,'))*))91:+9.*4:1A1+9,1))2*:..)))2*:31.-1)4131)1))2*:3)"'))
    {for(ord){$i+=$_&7;grep(vec($s,$i++,1)=1,1..($_>>3)-4);}}print"$s\n";

  4. #4
    Join Date
    Mar 2011
    Posts
    61
    Here's another example.

    Code:
    // first create a Person constructor, which takes two arguments and sets them as properties of the created instance
    function Person(name, age){
     this.name = name;
     this.age = age;
    };
    // add a function to the prototype, so that it shares the function on all instances of Person.
    Person.prototype.sayName = function(){return 'Hi, I\'am '+this.name};
    
    // next create a Worker constructor
    function Worker(name, age, company){
     Person.call(this, name, age);         // call the Person contructor with the instance to be created as relevant object, so it can set the name and age properties.
     this.company = company;               // add another property
    }
    Worker.prototype = new Person;         // set the Worker prototype to the Person prototype
    Worker.prototype.constructor = Worker; // set the constructor back to Worker
    Worker.prototype.sayCompany = function(){return 'I work at '+this.company} // extend the Worker prototype
    Worker.prototype.sayName = function(){ //overwrite the sayName function
     return Person.prototype.sayName.call(this) +'. '+ this.sayCompany();      // call the Person.sayName, and add some
    };
    One thing to consider is that when inheriting from another constructor using Sub.prototype = new Base, can cause problems the Base constructor requires an argument.
    for example:
    Code:
    function Sub(){}.prototype = new function Base(date){date.getDay()}; // Error: undefined does not have the method getDay();
    That's why it's better to use an intermediate constructor.
    Code:
    function InterMediate(){};
    Intermediate.prototype = Base.prototype
    Sub.prototype = new Intermediate;
    Which can be used to create an extend function, which also points the constructor to the right function
    Code:
    var extend = (function extend(I){
     return function extend(A, B){
      I.prototype = B.prototype;
      A.prototype = new i;
      A.prototype.constructor = A;
     };
    }(function(){}));
    Code:
    //Worker.prototype = new Person;
    //Worker.prototype.constructor = Worker;
    // becomes:
    extend(Worker, Person);

  5. #5
    Join Date
    Jun 2005
    Posts
    11
    Thanks for the reactions.

    I got the answer to my question.
    PHP Code:
    function Dog(name) { 
        
    this.name name


    Dog.prototype.bark = function () { 
        
    // some code 
    }; 
    This way I can create 1000 Dog objects which all share the same function (in memory).

    Also thanks for the book suggestions. The book I was talking about actually was Javascript Patterns, which is a great book, but I'm missing some basics, so maybe I will buy the Object-Oriented Javascript book.

    Let's see if I understand it correctly now:
    If you do: Dog.prototype = ....
    - Then you're setting the prototype property of the Dog object.
    - The prototype property is pointing to an object, by default Object(), but you can set it to any other object.
    - You can add functions and properties to that object.
    - Each time a Dog object is created it gets all the functions and properties of the object that was set as prototype.
    Is this right?

    Thanks again all of you.

  6. #6
    Join Date
    Jul 2003
    Location
    The City of Roses
    Posts
    2,503
    Quote Originally Posted by stefan2 View Post
    Let's see if I understand it correctly now:
    If you do: Dog.prototype = ....
    - Then you're setting the prototype property of the Dog object.
    - The prototype property is pointing to an object, by default Object(), but you can set it to any other object.
    - You can add functions and properties to that object.
    - Each time a Dog object is created it gets all the functions and properties of the object that was set as prototype.
    Is this right?
    All correct. :-)
    for(split(//,'))*))91:+9.*4:1A1+9,1))2*:..)))2*:31.-1)4131)1))2*:3)"'))
    {for(ord){$i+=$_&7;grep(vec($s,$i++,1)=1,1..($_>>3)-4);}}print"$s\n";

  7. #7
    Join Date
    Jan 2007
    Location
    Wisconsin
    Posts
    2,120
    As much as I loathe the using the prototype property (it's ugly, roundabout, and unorganized looking), it not only comes with significant memory savings, but there's also a significant efficiency advantage when calling prototyped methods versus non-prototyped methods. In my benchmarks, it ranges between 13.5% and 21.7%.

    But, bear in mind that, while this is a really significant improvement percentage-wise, it's not a noticeable improvement unless you're writing incredibly "busy" code. If you're writing a game, for example, and tracking thousands or hundreds of thousands of objects, trying to call various methods on each of those objects hundreds or thousands of times per tick with 20 or 30 ticks/second, you may benefit from the 13% to 22% boost. And you may see even bigger gains if your application has started paging. Though, chances are your DOM/Canvas calls are the real overhead if you're pushing the limits.

    However, if you're NOT making in excess of 10 million method calls/second and are instead doing some simple AJAX stuff, polling for new message every 5 seconds or so, using the prototype isn't at all necessary. No one will notice a 22% efficiency gain on an overhead of 0.000022ms. And the same goes for the negligible amounts of extra memory you'll be consuming.


    Applications that are pushing the limits may very well see benefits from using the prototype version:
    Code:
    var ClassName = function() {
    }
    
    ClassName.prototype.method = function() {
    }

    However, most applications can stand to use the version that is, in my opinion, prettier and more organized:

    Code:
    var ClassName = function() {
      this.method = function() {
      }
    }
    One of the first things I was taught in my CS studies: The better solution is the one that best meets the applications specific needs. Sometimes it's efficiency, sometimes it's a small footprint, sometimes it's the solutions is fastest or easiest to implement, and sometimes it's just the code that's easiest to read and maintain.

  8. #8
    Join Date
    Jul 2003
    Location
    The City of Roses
    Posts
    2,503
    Quote Originally Posted by svidgen View Post
    One of the first things I was taught in my CS studies: The better solution is the one that best meets the applications specific needs. Sometimes it's efficiency, sometimes it's a small footprint, sometimes it's the solutions is fastest or easiest to implement, and sometimes it's just the code that's easiest to read and maintain.
    Fortunately, these days we don't need to choose between readability and performance. There are libraries that will let you write code in the style you prefer, while still using prototypes under the hood.

    Here's one I wrote some time ago.

    PHP Code:
    var BaseFn;

    // Private scope
    (function () {
        
    BaseFn extendFunction.call(Object, {
            
    init: function () {
                
    // Stub
            
    }
        });

        function 
    extendFunction(overrides) {
            
    // Create subtype constructor
            
    function Subtype() {
                return 
    this.init.apply(thisarguments);
            }

            
    // Make subtype extendable
            
    Subtype.extend extendFunction;

            
    // Create subtype prototype that inherits from supertype prototype
            
    Subtype.prototype extendObject(this.prototypeoverrides);

            
    // Fix constructor property
            
    Subtype.prototype.constructor Subtype;

            return 
    Subtype;
        }

        function 
    extendObject(ooverrides) {
            
    // Spawn
            
    function F() {}
            
    F.prototype o;
            var 
    subtype = new F();

            
    // Augment
            
    if (overrides) {
                for (var 
    p in overrides) {
                    
    subtype[p] = overrides[p];
                }
            }

            
    // Reference supertype
            
    subtype.$super o;

            return 
    subtype;
        }
    }()); 
    Using this, our code from this post could look like this:

    PHP Code:
    var Animal BaseFn.extend({
        
    move: function () {
            
    // some code
        
    }
    });

    var 
    Dog Animal.extend({
        
    init: function (name) {
            
    this.$super.init.call(this);
            
    this.name name;
        },

        
    bark: function () {
            
    // some code
        
    }
    }); 
    for(split(//,'))*))91:+9.*4:1A1+9,1))2*:..)))2*:31.-1)4131)1))2*:3)"'))
    {for(ord){$i+=$_&7;grep(vec($s,$i++,1)=1,1..($_>>3)-4);}}print"$s\n";

  9. #9
    Join Date
    Jul 2008
    Location
    urbana, il
    Posts
    2,787
    the code you posted is broken: a has no add method...

    keep in mind that there is a higher property-name resolution cost on prototypes than there is on own properties. If a prototype get chained 2 or 3 (or 20) levels deeps, there's a lot of potentials to strike off the list before the correct prototype appears.

    your two methods are identical pure functions, so there's no reason to tote them around with the objects.
    the main advantage of binding methods in-constructor is making use of the privileged pattern, aka having access to closure inside the constructor.

    if you don't need closure, but do need direct object association, use prototypes.
    if you don't need either (the example given doesn't), use a 3rd-party method container:
    Code:
    fn={
      add : function(b) {
        return this.a + b;
      }
    };//end fn
    
    
    alert(
    
      fn.add.call(
         {a:1},   2
      )
    
    ) //shows: 3

  10. #10
    Join Date
    Jun 2005
    Posts
    11
    I won't be needing that many objects, but I guess I can better do things the efficient way now, than rewriting my entire code of the game too make it run properly, in the end.

    Also I will be enclosing each constructor + prototype code into a separate file. At least for all the game-objects and the helper-objects like Vector, Table, Stack, etc. This way the prototype code isn't as chaotic, everything just has it's own file.

  11. #11
    Join Date
    Jan 2007
    Location
    Wisconsin
    Posts
    2,120
    Quote Originally Posted by rnd me View Post
    the code you posted is broken: a has no add method...
    Yup ... typo. The benchmarked code is correct.

  12. #12
    Join Date
    Jan 2007
    Location
    Wisconsin
    Posts
    2,120
    @rnd_me

    I understand your points, but they're not terribly relevant. This is a question of efficiency and memory footprint. If you absolutely need closure, you'll obviously use a syntax that provides closure. Otherwise you need to balance between maintainability (self-documenting, well-organized code) and efficiency. Most modern language do a great job of making things things the same. But even then, as is the case with JavaScript, there are nuances that have a clear edge.

    Whether those edges are noticeable when they account for less than 1% of your execution time ... ?

    keep in mind that there is a higher property-name resolution cost on prototypes than there is on own properties. If a prototype get chained 2 or 3 (or 20) levels deeps, there's a lot of potentials to strike off the list before the correct prototype appears.
    An interesting concern. I'm not 100% sure it's valid though. Even in a true OOP language, I would expect most compilers to stuff a reference to the most relevant versions of a method directly in the end-class. And, since JavaScript has no implicit mechanism for inheritance, I'm not sure how this would prototype chain would occur unless you explicitly built it.

    Could you show us what inheritance pattern(s) lead to this sort of prototype chaining? I'd be interested to benchmark it.

  13. #13
    Join Date
    Jan 2007
    Location
    Wisconsin
    Posts
    2,120
    This syntax is actually not bad from an organizational standpoint, and it uses the prototype without the help of a library.

    var MyClass = function() {};
    MyClass.prototype = {
    constructor: function(p) {
    this.p = p || 123;
    },

    add: function(a) {
    return this.p + a;
    },

    subtract: function(a) {
    return this.p - a;
    }
    }
    Still left to figure out a mechanism for inheritance ...

  14. #14
    Join Date
    Jul 2008
    Location
    urbana, il
    Posts
    2,787
    Quote Originally Posted by svidgen View Post
    And, since JavaScript has no implicit mechanism for inheritance, I'm not sure how this would prototype chain would occur unless you explicitly built it.

    does prototypal inheritance not count in your book or what did you mean to say instead? it also has lexical inheritance for inner functions, complete with closure. i'd call that pretty good support for inheritance myself, but i guess we all have opinions...
    the chain it the game, it's one of the first things JavaScripts learn. my tagline for years has been "working on the chain..."; the prototype chain. i guess that one went over your head.. maybe i should be more humanistic, or stop trying to be funny; it often fails.


    Quote Originally Posted by svidgen View Post
    Could you show us what inheritance pattern(s) lead to this sort of prototype chaining? I'd be interested to benchmark it.
    sure. basic javascript tutorials offer a very common inheritance chain pattern:

    Code:
    function life(){
     this.alive=true;
     this.die=function(){this.alive=false;}
    }
    
    function mammal(){
     this.hair=true;
    }mammal.prototype=new life;
    
    
    function human(){
     this.thumb=true;
    }human.prototype=new mammal;
    
    
    bob=new human;
    alert(bob.alive)

    for bob.alive to not be undefined, it had to check:
    Code:
    bob.own
    human.proto
    mammal.proto
    life.proto

    compare the chain for
    Code:
     bob.die
    vs
    Code:
     methods:{die: function die(){this.alive=false}}; methods.die.call(bob)
    there, we jump right to methods.own, no prorotype hopping required. so it is faster.

    i'd bet your test would flush out the theory, given a sufficient production testing environment and not just a simple loop-based bench mark that JIT and tracing cheats at...


    so, bob can build faster without prototype (as your testing confirmed), it uses less memory to not have the die method on any instances, and if the bundled pure function avoids other-wise present closures, it's sure to execute faster as well...
    Last edited by rnd me; 07-06-2011 at 04:24 PM.

  15. #15
    Join Date
    Jan 2007
    Location
    Wisconsin
    Posts
    2,120
    This is starting to detract from my focus at work ...

    Remember that part of the issue is writing clear, well organized code. Jeff's claim is that you can accomplish this without sacrificing on performance. Also, your final example (method:{ ... }), while it would seem to provide the most efficient solution, actually benchmarks poorly on method calls -- a 15.5% hit (possibly because all your method calls go through call() or apply()). And more importantly, it doesn't lend itself well to well-organized, maintainable code, in my opinion.

    I would actually argue that it's messier than just using the prototypes -- which actually benchmarked on par with local methods. If you read up, you'll see that it's object instantiation that suffers the big hit without prototyping (twice as long to create SIMPLE objects). The only issue noted with local methods is the risk of flooding the memory, which could eventually cause paging, crashes, or nasty messages about runaway scripts.

    i'd bet your test would flush out the theory, given a sufficient production testing environment and not just a simple loop-based bench mark that JIT and tracing cheats at...
    While you need to be careful to consider the effects of the benchmark's simplicity and take it with a grain of salt, using something more complicated than a loop can "covertly" introduce unforeseen bias, which may ultimately never be noticed in the benchmark analysis. That said, the simplistic benchmark is more likely to reveal a difference with an identifiable source. However, a more complex benchmark, such testing component variation A vs B in product X will reveal how each will actually perform in X. But, if A and B will each require a great deal of work to hack in (such as changing the entire manner in which you deal with Classes/objects), it seems best to me to have the results from an over-simplistic benchmark to set me in the presumably more efficient and maintainable direction.

    I also ran another very quick benchmark to local variable access versus one several steps up the prototype chain. The difference isn't detectable within the first million calls. Ramping the test up to 5 million calls made it detectable, but it's only about a 0.1% loss for using elements from a prototype 3 links up the chain. Validation that you could see significant efficiency concerns if you have a very long prototype chain (I wouldn't expect to see any real impact before 20 or 30 ancestors are in play). It's a negligible difference for most of us--and very likely offset by the memory footprint concerns at that point where it becomes a concern.

    so, bob can build faster without prototype (as your testing confirmed)
    Opposite of that. Bob builds much faster with prototype. Over 2 times faster with simple classes. Likely more than 2 time faster with larger classes. No property copying required. And presumably very little memory allocation required.

    And back to the beginning ...
    does prototypal inheritance not count in your book or what did you mean to say instead?
    OK. Prototypes provide some inheritance. But, it's really a stretch to equate it with more common notions of inheritance. It doesn't provide clean access to base/super methods.

    So, the OP is about efficiency. The most efficient solution, to the extent that I have been able to benchmark, is to use prototypes "properly." The biggest performance difference is in object instantiation, wherein the gap STARTS and around 100% difference for simple classes. (It presumably widens as the classes become more complex.) Helper libraries and techniques can introduce some efficiency hits; however, they can also offer much cleaner looking code while saving the BIG 100+% object instantiation and memory footprint hits. And they can also offer mechanisms that are otherwise unnatural, such as this.method() containing a call to this.$super.method()--moot point if those sorts of "normal" inheritance mechanisms aren't required.

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
HTML5 Development Center



Recent Articles