www.webdeveloper.com
Page 1 of 2 12 LastLast
Results 1 to 15 of 23

Thread: Object literals and inheritance

Hybrid View

  1. #1
    Join Date
    Mar 2011
    Posts
    19

    Object literals and inheritance

    Hi all, sorry for the newbie question. I really love the way Javascript objects inherit directly from other objects, rather than via a class structure. It seems lie a much more sensible way of doing things in an interpreted language. I have a small question though about something that seems to me like it should work but doesn't. If I define an object in an object literal like:

    var father = {
    i: "rudi",
    f: function() {
    alert(this.i)
    }
    }

    father.f();
    >> "rudi"

    I'm basically declaring an associative array or hash here containing 2 variables, one of which points to a string, and the other to a function I think. But if I do:

    var son = {
    prototype:father
    }

    son.f();

    I get an error. Surely the prototype is an variable with the object as it's scope. If so, why can I not set it directly in the object literal? It clearly doesn't work, but Id love to know why.

    Many thanks

  2. #2
    Join Date
    Dec 2003
    Location
    Bucharest, ROMANIA
    Posts
    15,428
    Oh. So you want to "clone" an object, right? I guess you may use a "vector" constructor for that, in order to separate the references. Something like:
    Code:
    <script type="text/javascript">
    function Clone(){}
    function clone(obj) {
        Clone.prototype = obj;
        return new Clone();
    }
    
    var father = {
    i: "rudi",
    f: function() {
    alert(this.i)
    }
    }
    var son=clone(father);
    son.f()
    </script>
    Now you have 2 independent objects, with the first object (father) delegating its prototype to the second one (son).

  3. #3
    Join Date
    Mar 2011
    Posts
    19
    Ah, this is where I come unstuck, I'm a Java/Ruby developer so do please bear with me. In Java, everything eventualy inherits from the Object class.

    My assumption was that, since I haven't explicitly set the prototype of father (it's a literal) it's prototype would be Object, which I assume is the base of the inheritance hierarchy tree.

    I assumed then that father.prototype would return a pointer to the base of the inheritance hierarchy. Is this way off base?

  4. #4
    Join Date
    Jul 2003
    Location
    The City of Roses
    Posts
    2,503
    Quote Originally Posted by goldfidget View Post
    My assumption was that, since I haven't explicitly set the prototype of father (it's a literal) it's prototype would be Object, which I assume is the base of the inheritance hierarchy tree.

    I assumed then that father.prototype would return a pointer to the base of the inheritance hierarchy. Is this way off base?
    You're not off base. I think Kor merely misspoke. In his first example, the "son" object inherits from the "father" object, and the "father" object inherits from Object.prototype, exactly as you expected.
    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";

  5. #5
    Join Date
    Jul 2003
    Location
    The City of Roses
    Posts
    2,503
    I think Crockford explains this topic better than anyone, and his videos may help.

    http://video.yahoo.com/watch/111585/1027823
    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";

  6. #6
    Join Date
    Dec 2003
    Location
    Bucharest, ROMANIA
    Posts
    15,428
    Quote Originally Posted by Jeff Mott View Post
    You're not off base. I think Kor merely misspoke. In his first example, the "son" object inherits from the "father" object, and the "father" object inherits from Object.prototype, exactly as you expected.
    I did exactly what OP expected. I don't see where's the misspoke there. The OP wanted to create a new object which should inherit the prototype of another. So I did.

  7. #7
    Join Date
    Jul 2003
    Location
    The City of Roses
    Posts
    2,503
    Quote Originally Posted by Kor View Post
    I don't see where's the misspoke there.
    You said the "father" object delegates to the "son" object, but it's the other way around. The son delegates to the father, and the father delegates to Object.prototype.

    It's a seemingly small detail, but it caused some confusion.
    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";

  8. #8
    Join Date
    Dec 2003
    Location
    Bucharest, ROMANIA
    Posts
    15,428
    That happens, probably because prototype is a pre-built object, as being a fundamental property of all the objects. It can not be created ab-nihilo. It is a vector rather than a usual property, and it is created automatically when you create an object.
    When you write:
    Code:
    var son={
    prototype:father
    }
    You simply give the object a new custom property, called "prototype", but you don't delegate the prototype from an object to another Thus, if you need to check that, simply:
    Code:
    var father = {
    i: "rudi",
    f: function() {
    alert(this.i)
    }
    }
    var son={
    prototype:father
    }
    son.prototype.f()
    Last edited by Kor; 03-04-2011 at 09:33 AM.

  9. #9
    Join Date
    Mar 2011
    Posts
    19
    Ah yes, you're right. prototype is instantiated as a custom property. One thing still bothers me, and sorry again for the silly questions. What is the purpose of the new operator here? I would have thought that simply setting the prototype property would be sufficient to create an inheritance tree, but I see that it is not.
    Code:
    var father = {
      i: "rudi",
      f: function() {
        alert(this.i)
      }
    }
    
    var son = {
    }
    
    son.prototype = father;
    son.f();
    
    ==> son.f is not a function
    In Java, the new operator would create a new object with the specified class, and invoke it's constructor. In Javascript the object is already instantiated so doesn't need to be created, and I should be able to declare it's parent in the inheritance tree by setting the prototype operator (though I see that I can't). What does new do in this context?

    Many thanks, I really appreciate the time you're taking
    Last edited by goldfidget; 03-04-2011 at 11:41 AM.

  10. #10
    Join Date
    Feb 2003
    Location
    Michigan, USA
    Posts
    5,755

  11. #11
    Join Date
    Jul 2003
    Location
    The City of Roses
    Posts
    2,503
    Quote Originally Posted by toicontien View Post
    Nice. That's a great resource and well worth a read. Though, I did want to caution against their method for achieving inheritance.

    Code:
    Son.prototype = new Father();
    This line, where we set up inheritance, has an unfortunate side-effect: it executes the Father constructor. What if, for example, the name property wasn't optional?

    Code:
    function Father(name) {
        if ( ! name) throw new Exception();
        this.name = name;
    }
    In this specific case, we could work around the issue by using a dummy name—"John Doe" perhaps—but in real-life scenarios, it may not be so easy. A constructor might manipulate the DOM or send an Ajax request, and we don't want all that to happen when we're only setting up inheritance. We need a way to create inheritance without executing the constructor.

    I think Douglas Crockford was the first to come up with this workaround:

    Code:
    function object(o) {
        function F() {}
        F.prototype = o;
        return new F;
    }
    What this function does is return a new, empty object that inherits from the given object. So now our inheritance setup would look like this:

    Code:
    Son.prototype = object(Father.prototype);
    Of course, there are still many ways to improve on this pattern. I like to re-package the object function into an "extend" method.

    Code:
    var BaseObj = {
        extend: function () {
            function F() {}
            F.prototype = this;
            return new F;
        }
    };
    
    var Father = BaseObj.extend();
    
    var Son = Father.extend();
    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";

  12. #12
    Join Date
    Mar 2011
    Posts
    19
    Quote Originally Posted by Jeff Mott View Post

    I think Douglas Crockford was the first to come up with this workaround:

    Code:
    function object(o) {
        function F() {}
        F.prototype = o;
        return new F;
    }
    What this function does is return a new, empty object that inherits from the given object. So now our inheritance setup would look like this:

    Code:
    Son.prototype = object(Father.prototype);
    Of course, there are still many ways to improve on this pattern. I like to re-package the object function into an "extend" method.

    Code:
    var BaseObj = {
        extend: function () {
            function F() {}
            F.prototype = this;
            return new F;
        }
    };
    
    var Father = BaseObj.extend();
    
    var Son = Father.extend();
    Ah, and this is actually a lot nicer than Crockford's naked object() function solution. So if I'm getting this correctly you should be able to just extend the Object object to give everything the extend method.

    Code:
    Object.prototype.extend = function() {
      function F() {}
      F.prototype = this;
      return new F;
    }
    
    father = {}
    father.hello = function() {
      alert("Rudi");
    }
    father.hello();
    ==> Rudi
    
    son = father.extend() 
    son.hello()
    ==> Rudi
    Now, rather than saying daimler = object(car), I can say daimler = car.extend() which is clearly much nicer.

    Would there be a way to pass an object literal to the extend method and have all it's members added to the child object, or would that mean that this would always point to the original closure from within those methods?

    One other thing that's confusing me. Why do I have to add the method to Object.prototype rather than simply Object? It's as though nothing inherits from Object, but everything inherits from Object.prototype. Does Object hang off the side of the inheritance tree, and if so, why?

  13. #13
    Join Date
    Jul 2003
    Location
    The City of Roses
    Posts
    2,503
    Quote Originally Posted by goldfidget View Post
    So if I'm getting this correctly you should be able to just extend the Object object to give everything the extend method.
    Yes. In fact, that's how prototype objects were designed to be used. Though, eventually developers started to notice a problem. When you iterate over an object, you get not only its own properties, but everything attached to the prototype as well.

    Code:
    Object.prototype.extend = function() {
        function F() {}
        F.prototype = this;
        return new F;
    }
    
    var abc = {a: 'a', b: 'b', c: 'c'};
    
    for (var p in abc) {
        alert(p); // alerts a, b, c, and extend
    }
    One way developers suggested avoiding this problem was to use the hasOwnProperty method, and use it religiously.

    Code:
    for (var p in abc) {
        if (abc.hasOwnProperty(p)) {
            alert(p); // alerts a, b, c
        }
    }
    And some developers did that, but not everyone did, and developers started to painfully realize that if they attached new properties to the built-in prototypes, then their code would likely break someone else's code, because you never know if all the other code on a page checks hasOwnProperty or not. Today, it's considered taboo to alter the built-in prototypes. If you can avoid it, then your code will be safer and more portable.

    Quote Originally Posted by goldfidget View Post
    Would there be a way to pass an object literal to the extend method and have all it's members added to the child object
    There sure is.

    Code:
    var BaseObj = {
        extend: function (newProperties) {
            function F() {}
            F.prototype = this;
            var subtype = new F;
            
            if (newProperties) {
                for (var p in newProperties) {
                    subtype[p] = newProperties[p];
                }
            }
            
            return subtype;
        }
    };
    Quote Originally Posted by goldfidget View Post
    Why do I have to add the method to Object.prototype rather than simply Object?
    When you make a new instance of something — var instance = new Constructor() — remember that the instance inherits from Constructor.prototype. Object is the same way. "Object" is the constructor function, and Object.prototype is the object from which all instances inherit.
    Last edited by Jeff Mott; 03-05-2011 at 05:33 PM.
    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";

  14. #14
    Join Date
    Feb 2003
    Location
    Michigan, USA
    Posts
    5,755
    Quote Originally Posted by Jeff Mott View Post
    Nice. That's a great resource and well worth a read. Though, I did want to caution against their method for achieving inheritance.

    Code:
    Son.prototype = new Father();
    This line, where we set up inheritance, has an unfortunate side-effect: it executes the Father constructor. What if, for example, the name property wasn't optional?

    Code:
    function Father(name) {
        if ( ! name) throw new Exception();
        this.name = name;
    }
    In this specific case, we could work around the issue by using a dummy name—"John Doe" perhaps—but in real-life scenarios, it may not be so easy. A constructor might manipulate the DOM or send an Ajax request, and we don't want all that to happen when we're only setting up inheritance. We need a way to create inheritance without executing the constructor.

    I think Douglas Crockford was the first to come up with this workaround:

    Code:
    function object(o) {
        function F() {}
        F.prototype = o;
        return new F;
    }
    What this function does is return a new, empty object that inherits from the given object. So now our inheritance setup would look like this:

    Code:
    Son.prototype = object(Father.prototype);
    Of course, there are still many ways to improve on this pattern. I like to re-package the object function into an "extend" method.

    Code:
    var BaseObj = {
        extend: function () {
            function F() {}
            F.prototype = this;
            return new F;
        }
    };
    
    var Father = BaseObj.extend();
    
    var Son = Father.extend();
    I've done this sort of coding before, where a constructor throws an error if it doesn't receive one of the arguments. I highly recommend against this sort of behavior. A constructor should never do anything even semi intelligent. You never know when you might want to generate an object instance and have it on hand before you have acquired all of that object's dependencies.

    Essentially, objects have 5 phases to their lifecycle:

    1) Instantiation: The object is born. It might acquire some dependencies, but it remains largely useless. It simply exists.

    2) Configuration: All dependencies are acquired and any other optional properties that can alter the object's behavior are set.

    3) Initialization: This optional phase is where code outside of the object (client code) tells the object to start doing what it was born to do.

    4) Use: Client code can interact with the object.

    5) Destruction: This phase is not hard wired in JavaScript, but I often include "destructor" functions with objects. It's here where DOM event handlers are unbound and object pointers are nullified, which readies the object for natural garbage collection by the JavaScript interpreter.

    For the purposes of JavaScript in a browser, the boldface phases are really the only required phases. Depending on how complex the object's purpose is, steps 3 and 5 might need to be added.

    With having the constructor function do nothing but acquire dependencies while not throwing errors, you can set up prototypal inheritance the easy way, by setting a child class's prototype object to a new instance of the parent class. But you need to be sure to call the parent class's object constructor function in the child classes object constructor function so that object properties set in the parent class on the child class's prototype don't become shared properties of all the child class's instances.

  15. #15
    Join Date
    Jul 2003
    Location
    The City of Roses
    Posts
    2,503
    Quote Originally Posted by toicontien View Post
    With having the constructor function do nothing but acquire dependencies while not throwing errors, you can set up prototypal inheritance the easy way...
    This may be just personal preference, but I don't think the 3-line object function is much more difficult than "the easy way." object(Father.prototype) or Father.extend() seems just as easy to me as new Father(), but with the added benefit that it doesn't enforce arbitrary restrictions on the constructors.
    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";

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