Gmail Calendar Documents Reader Web more »
Recently Visited Groups | Help | Sign in
Google Groups Home
Message from discussion Prototypal inheritance and mutable objects
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
Richard Cornford  
View profile  
 More options Mar 30 2008, 1:35 pm
Newsgroups: comp.lang.javascript
From: "Richard Cornford" <Rich...@litotes.demon.co.uk>
Date: Sun, 30 Mar 2008 18:35:59 +0100
Local: Sun, Mar 30 2008 1:35 pm
Subject: Re: Prototypal inheritance and mutable objects

Nick Fletcher wrote:
> I've recently been working on a fairly large JavaScript project
> using prototypal inheritance.

As javascript is a language that uses prototypal inheritance it could be
argued that if you use the language you are using prototypal
inheritance, and so that saying as much would be redundant.

> I've been using this commonly seen clone (a.k.a. object) function
> for implementing the inheritance hierarchies:

>    function clone(o) {
>        function F() {}
>        F.prototype = o;
>        return new F();
>    }

Which is an example of the process that clearly expresses what is being
done, but is not particularly efficient as it creates a new - F -
function each time it is executed, but all of those - F - functions are
essentially identical. If this is to be done often then a more efficient
approach would be to only create a single - F - function and put it
where it could not be modified by external code. I.E.:-

var clone = (function(){
    function F(){}
    return (function(o){
        F.prototype = o;
        return new F();
    });

})();

You have not said why you are using your - clone - function (why you
considered its use appropriate in your context and superior to the may
other approaches possible).

> The problem I've come across is that mutable objects in my
> "Prototype Object" get shared for all inheriting instances
> of the object.

Yes, that is what happens here. Indeed it is one of the aspects of this
strategy that make it useful as it avoids the need to explicitly assign
references to commonly shared object.

> For
> example, a rudimentary collections API:

>    /**
>     *  Collection Prototype Object
>     */
>    var Collection = {
>        items: [], // Gets used by everyone
>        add: function (item) {
>            this.items[this.items.length] = item;
>        },
>        remove: function (item) {
>            var count = 0;
>            for (var i = 0, len = this.items.length; i < len; i++) {
>                if (item === this.items[i]) {
>                    this.items.splice(i, 1);
>                    i--;
>                    len--;
>                }
>            }
>        }
>    };

>    /**
>     * An indexed collection
>     */
>    var List = clone(Collection);
>    List.insert = function (item, index) {
>        this.items.splice(index, 0, item);
>    };
>    List.get = function (index) {
>        return this.items[index];
>    };

>    /**
>     * Contains a unique set of elements (no duplicates)
>     */
>    var Set = clone(Collection);
>    Set.add = function (item) {
>        for (var i = 0, len = this.items.length; i < len; i++) {
>            if (item === this.items[i]) {
>                return;
>            }
>        }
>        this.items[this.items.length] = item;
>    };

> Any cloned objects that I create will all share the same 'items'
> array.

>    var list = clone(List);
>    var set = clone(Set);

>    list.add("one");
>    alert(set.items.length === 0); // false, it's 1

But this example is far too simplistic to justify the use of a - clone -
method as a simple constructor/prototype definition would get the job
done with a negligible difference in the complexity of the code used
(and could be argued to be clearer as it would be the more natural
approach when using javascript). I.E.:-

function Collection(){
    this.items = [];

}

Collection.prototype.add = function(){
    this.items[this.items.length] = item;
};

Collection.prototype.remove = function(){
    var count = 0;
    for (var i = 0, len = this.items.length; i < len; ++i) {
        if (item === this.items[i]) {
            this.items.splice(i, 1);
            --i;
            --len;
        }
    }

};

function List(){
    this.items = [];
}

List.prototype = new Collection();
//or:-  List.prototype = clone(Collection.prototype);
List.prototype.insert = function (item, index) {
    this.items.splice(index, 0, item);
};

List.prototype.get = function (index) {
    return this.items[index];

};

function Set(){
    this.items = [];
}

Set.prototype = new Collection();
//or:-  Set.prototype = clone(Collection.prototype);
Set.prototype.add = function (item) {
    for (var i = 0, len = this.items.length; i < len; i++) {
        if (item === this.items[i]) {
            return;
        }
    }
    this.items[this.items.length] = item;

};

var list = new List();
var set = new Set();

list.add("one");
alert(set.items.length === 0); // true, inevitably.

> One solution would be for all cloned objects to explicitly
> set their own copies of the mutable objects, which can be a
> bit of a bother (especially if there are a lot).

> My current working solution is to add a 'create' method to
> the root of my inheritance hierarchy. This method does the
> job of cloning "this" and adding any mutable objects to the
> cloned object:

>    var Collection = {
>        create: function () {
>            var collection = clone(this);
>            collection.items = []; // add mutable objects here

>            return collection;
>        },

>        // add and remove
>    };

> Now I can create as many instances as I want and they will all have
> their own mutable objects. For example:

>    var list = List.create();
>    var set = Set.create();

>    list.add("one");
>    alert(set.items.length === 0); // true

So you have an approach that does not employ a constructor function and
so does not get the set-up on instanceiation that  comes with the -
new - operator and constructor functions, so you have added another
function that is used in place of the constructor. That seems to be
doing a lot of work to get back to what the language would have given
you form day one.

> There's still the problem of having to attach all super mutable
> objects to an inheriting object if you want to add another mutable
> property:

>    Sub = clone(Collection);
>    Sub.create = function () {
>        var collection = clone(this);
>        collection.items = [];
>        collection.otherArray = [];

>        return collection;
>    };

> This could be quite annoying if you have a lot of mutable objects that
> need to be added (which I think is true without this pattern anyway).
> In order to avoid duplicating all that code again, a (not so clean)
> possibility is a pseudo-super call using apply:

>    Sub = clone(Collection);
>    Sub.create = function () {
>        var sub = Collection.create.apply(this); // Attach other
> mutable objects
>        sub.otherArray = [];

>        return sub;
>    };

> My questions are: Is this a good approach?

A good approach to what exactly? I use this style of cloning when I want
multiple object instances that share some sort of runtime determined
set-up or configuration so that they do not need to go through that
process again when each new instance is created, but then the object
being cloned is either never used as an actual instance or the cloning
process has to mask instance specific aspects of the original (requiring
a more elaborate cloning process). I do not use this approach for
inheritance relationships that could be statically defined in the source
code.

> What are my alternatives?

The alternatives depend on the 'why', but there will be hundreds (and
most of them, but probably not all of them, will be worse).

> Am I completely out of my mind?

Probably not (and certainly not for asking). However, keep a few facts
in mind; client-side javascript is vary rarely so complex that it needs
deep inheritance hierarchies, the language has a natural inheritance
mechanism that should not be deviated from until doing so has some
manifest advantage (that can be clearly stated), and that any single
'formal inheritance strategy' applied across the board is likely to act
as a straightjacket and preclude possibilities that could be useful
and/or interesting.

Richard.


    Reply to author    Forward  
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.

Create a group - Google Groups - Google Home - Terms of Service - Privacy Policy
©2009 Google