www.webdeveloper.com
Page 2 of 2 FirstFirst 12
Results 16 to 23 of 23

Thread: Object literals and inheritance

  1. #16
    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?

  2. #17
    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";

  3. #18
    Join Date
    Feb 2003
    Location
    Michigan, USA
    Posts
    5,773
    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.

  4. #19
    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";

  5. #20
    Join Date
    Feb 2003
    Location
    Michigan, USA
    Posts
    5,773
    Quote Originally Posted by Jeff Mott View Post
    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.
    True, but then every single "class" in JavaScript has a common dependency on the Object.prototype.extend() function. For your own library of code that may not be a problem, but when you start using code from several libraries at once then things start to get muddled. I always opt for the smallest number of dependencies possible. Since with a little care you can eliminate the need for a JavaScript function to set up inheritance, I just make my constructors a tad more flexible. This greatly increases the portability of the code I write, which is advantageous when you work in a multi developer environment with a boat load of web apps.

    However, if you work with a very small group, there's no harm in a JavaScript function to create inheritance.

    Incidentally, if you don't need to support IE, this also sets up inheritance:

    Code:
    function Parent(name) {
        this.name = name;
    }
    
    Parent.prototype = {
        sayName: function() {alert("Hi, my name is " + this.name)}
    };
    
    function Son(name, parent) {
        Parent.call(this, name);
        this.parent= parent;
    }
    
    Son.prototype = {
        __proto__: Father.prototype // <-- Son inherits from Parent
    
        sayParent: function() {
            alert("My parent is " + this.parent.sayName());
        }
    };
    
    var s = new Son("Ed", new Parent("Dave"));
    
    alert(s instanceof Parent);
    s.sayParent();
    The __proto__ property is read/write in non IE browsers.

  6. #21
    Join Date
    Mar 2011
    Posts
    19
    Quote Originally Posted by toicontien View Post
    Incidentally, if you don't need to support IE, this also sets up inheritance:

    Code:
    function Parent(name) {
        this.name = name;
    }
    
    Parent.prototype = {
        sayName: function() {alert("Hi, my name is " + this.name)}
    };
    
    function Son(name, parent) {
        Parent.call(this, name);
        this.parent= parent;
    }
    
    Son.prototype = {
        __proto__: Father.prototype // <-- Son inherits from Parent
    
        sayParent: function() {
            alert("My parent is " + this.parent.sayName());
        }
    };
    
    var s = new Son("Ed", new Parent("Dave"));
    
    alert(s instanceof Parent);
    s.sayParent();
    The __proto__ property is read/write in non IE browsers.
    This is a nice syntax, as it lets you declare the whole inheritance hierarchy in litereals. From what I can see, it appears to work in Webkit and Gecko, but not IE or Opera.

    Just out of interest, is there a good design reason for not exposing the __proto__ property? If I was inventing a prototype based language, I would include that as a language feature.

  7. #22
    Join Date
    Feb 2003
    Location
    Michigan, USA
    Posts
    5,773
    I have no idea why it isn't read/write. It might have been one of those gray areas where no existing standard spelled out what should happen at the time the browsers were developed. Maybe they had the best intentions but was a feature that got axed in the name of shipping the software on time. Who knows.

  8. #23
    Join Date
    Mar 2011
    Posts
    19
    Quote Originally Posted by toicontien View Post
    I have no idea why it isn't read/write. It might have been one of those gray areas where no existing standard spelled out what should happen at the time the browsers were developed. Maybe they had the best intentions but was a feature that got axed in the name of shipping the software on time. Who knows.
    I was thinking about this last night. It seems to me that one good thing about not making prototype writable is that it absolutely guarantees that there will be no cycles in the inheritance graph since proto gets set at the moment of object creation.

    There would be a cost to checking for this each time __proto__ gets set. For example, in FF:

    Code:
    father = {}
    son = {}
    son.__proto__ = father;
    father.__proto__ = son;
    
    ==> cyclic __proto__ value

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