object augmentation and memory usage

Discussion in 'Javascript' started by Gawain Lavers, Jan 26, 2006.

  1. I have a script which uses a lot of object augmentation (in order to
    extend the functionality of DOM elements), and I'm clearly stressing
    out my browser (I rapidly get to a point where Firefox is telling me
    that the script is "unresponsive", often more than once. I'm not sure
    if it's simply execution time or memory usage.

    My question is this: if I augment an object thusly:

    function TestAug() {
    this.fun = function() {
    //do something
    }
    }

    function Test() {
    var test = new Object();
    TestAug.call(test);
    return test;
    }

    Then I create, oh, 800 Test objects, what is my memory usage? Are
    augmented members, such as fun, only stored in a single place, and
    merely accessed as instance methods through the magic of call, or is
    call basically adding a whole new copy of the function, with attendant
    time/memory usage, to the object passed to it? The books and resources
    I've found all discuss memory issues around class methods, instance
    methods, prototyping, etc., but are vague in discussing how call works.
    Gawain Lavers, Jan 26, 2006
    #1
    1. Advertising

  2. Gawain Lavers wrote:

    > I have a script which uses a lot of object augmentation (in order to
    > extend the functionality of DOM elements), and I'm clearly stressing
    > out my browser (I rapidly get to a point where Firefox is telling me
    > that the script is "unresponsive", often more than once. I'm not sure
    > if it's simply execution time or memory usage.


    The warning message is due to run-time always. However, the latter may be
    affected by increased memory usage, forcing the operating system to enlarge
    the swap file or to do more swapping than usual.

    > My question is this: if I augment an object thusly:
    >
    > function TestAug() {
    > this.fun = function() {
    > //do something
    > }
    > }
    >
    > function Test() {
    > var test = new Object();
    > TestAug.call(test);
    > return test;
    > }
    >
    > Then I create, oh, 800 Test objects, what is my memory usage?


    A considerably large one.

    > Are augmented members, such as fun, only stored in a single place, and
    > merely accessed as instance methods through the magic of call, or is
    > call basically adding a whole new copy of the function, with attendant
    > time/memory usage, to the object passed to it?


    With this approach, the creation of each new object `o' creates a new
    Function object (that `o.fun' refers to) via the `function' expression
    in turn. Use prototype objects to avoid that:

    function TestAug()
    {
    }

    TestAug.prototype.fun = function()
    {
    // do something
    }

    var test = new TestAug();


    PointedEars
    Thomas 'PointedEars' Lahn, Jan 26, 2006
    #2
    1. Advertising

  3. Gawain Lavers

    Evertjan. Guest

    Thomas 'PointedEars' Lahn wrote on 26 jan 2006 in comp.lang.javascript:

    > The warning message is due to run-time always.


    Eh?

    --
    Evertjan.
    The Netherlands.
    (Please change the x'es to dots in my emailaddress)
    Evertjan., Jan 26, 2006
    #3
  4. Yeah, prototype is clearly ideal, but I unfortunately don't have that
    option -- perhaps a more clear example:

    function DivAug() {
    this.fun = function() {
    //do stuff
    }
    }

    function MyDiv() {
    var div = document.createElement('div');
    DivAug.call(div);
    return div;
    }

    The idea I had was that rather than create an external infrastructure
    which would manage my DOM structure, I would simply augment the DOM
    elements so that they would know what to do with themselves.
    Unfortunately, I can't muck with a DOM element's prototype. If call
    creates a new copy of the functions methods with invocation, I guess
    I'm going to have to rethink everything from scratch. Thanks for the
    (albeit disappointing) info.

    By the way, does this not render object augmentation essentially
    useless in all but trivial cases?
    Gawain Lavers, Jan 26, 2006
    #4
  5. Gawain Lavers

    VK Guest

    Gawain Lavers wrote:
    > My question is this: if I augment an object thusly:
    >
    > function TestAug() {
    > this.fun = function() {
    > //do something
    > }
    > }
    >
    > function Test() {
    > var test = new Object();
    > TestAug.call(test);
    > return test;
    > }


    Any particular reason to make the approach so... interesting? ;-)


    function foobar() {
    // do something
    }

    function SuperAug() {
    this.bar = 'bar';
    }

    function TestAug() {
    SuperAug.call(this);
    this.foo = function() {
    //do something
    }
    this.foobar = foobar;
    }

    function test() {
    glbStack = new Array();
    for (var i=0; i<10000; i++) {
    glbStack.push(new TestAug());
    }
    alert('Created '+glbStack.length+' instances of TestAug');
    }


    > Then I create, oh, 800 Test objects, what is my memory usage? Are
    > augmented members, such as fun, only stored in a single place, and
    > merely accessed as instance methods through the magic of call, or is
    > call basically adding a whole new copy of the function, with attendant
    > time/memory usage, to the object passed to it?


    In the sample above each of 10,000 TestAug instances has its own copy
    of foo() method, its own copy of bar property (inherited from SuperAug)
    and there is one copy of foobar method for all 10,000 instances.

    > The books and resources
    > I've found all discuss memory issues around class methods, instance
    > methods, prototyping, etc., but are vague in discussing how call works.


    It is understandable because call(), apply(), .constructor, instanceof
    are members of other dark side of JavaScript which is not suitable to
    discuss in a rifined society. :)

    An understandable description may require some terrible words to
    pronounce. Without these words an understandable description is as
    difficult as qrfpevor gur vagrepbhefr cebprff jvgubhg hfvat gur jbeq
    CRAVF.

    :)

    But fully prototype-based inheritance is also well accepted and
    functional option.
    VK, Jan 26, 2006
    #5
  6. Gawain Lavers

    VK Guest

    Gawain Lavers wrote:
    > Yeah, prototype is clearly ideal, but I unfortunately don't have that
    > option -- perhaps a more clear example:
    >
    > function DivAug() {
    > this.fun = function() {
    > //do stuff
    > }
    > }
    >
    > function MyDiv() {
    > var div = document.createElement('div');
    > DivAug.call(div);
    > return div;
    > }
    >
    > The idea I had was that rather than create an external infrastructure
    > which would manage my DOM structure, I would simply augment the DOM
    > elements so that they would know what to do with themselves.
    > Unfortunately, I can't muck with a DOM element's prototype. If call
    > creates a new copy of the functions methods with invocation, I guess
    > I'm going to have to rethink everything from scratch.


    You will have to rethink your approach but not because of call() -
    call() merely replaces constructor context, so is it going to be a
    static method or not depends on constructor, not on call() usage.

    The catch is that at least IE doesn't allow you to treat DOM elements
    like JavaScript Objects (like one you're getting from new Object() ).
    What you can is:

    1) add allowed event listeners to a DOM element.
    2) attach external behaviors to DOM element (IE only)
    3) create JavaScript objects having DOM elements as their members

    The last approach may work universally for you. Say:

    function AmazingDIV() {
    this.$ = document.createElement('DIV');
    // ...
    this.addTo = function(trg) {trg.appendChild(this.$);}
    }

    var myDIV = new AmazingDIV();
    myDIV.addTo(document.body);

    And you are welcome to make addTo method static.
    VK, Jan 27, 2006
    #6
  7. Gawain Lavers wrote:
    > Yeah, prototype is clearly ideal, but I unfortunately don't
    > have that option -- perhaps a more clear example:
    >
    > function DivAug() {
    > this.fun = function() {
    > //do stuff
    > }
    > }


    The comment "do stuff" may not provide enough clarity as inner functions
    (declarations or expressions) have a specific role in javascript in that
    their use may forms closures:-

    <URL: http://www.jibbering.com/faq/faq_notes/closures.html >

    In this case it is difficult to see any advantage in using an inner
    function expression as the outer function does not have any parameters,
    local variables or inner function declarations, so the inner function's
    'environment' is empty. Because inner functions may form closures it is
    necessary for each such function object to be unique. The language's
    specification allows function objects to be re-used when it is
    impossible to externally observe the difference between a re-used
    function object and two instances of a function object. The observable
    difference would relate to the function's internal [[Scope]] property,
    and your inner functions would have indistinguishable [[Scope]]
    properties because their outer function has no parameters, local
    variables or inner function declarations. So it would be possible for an
    ECMAScript implementation to execute your code and not be creating a new
    function object with each execution of the - DivAug - method. However,
    experimentation has revealed no evidence of any ECMAScript
    implementations that take advantage of this permitted optimisation.

    Generally, you would not want to use an inner function at all unless you
    were taking advantage of its special status as an inner function, and
    the closure formed by its creation/use.

    > function MyDiv() {
    > var div = document.createElement('div');
    > DivAug.call(div);
    > return div;
    > }
    >
    > The idea I had was that rather than create an external
    > infrastructure which would manage my DOM structure,
    > I would simply augment the DOM elements so that they would
    > know what to do with themselves.


    Without any specific example it is not practical to say anything about
    this idea.

    > Unfortunately, I can't muck with a DOM element's prototype.


    You certainly cannot on most browsers, which is practically the same as
    not being able to at all.

    > If call creates a new copy of the functions methods with
    > invocation,


    Don't blame this on the function's call method, it is true of inner
    functions on all outer function calls. And it is a valuable and
    exploitable feature of the language.

    And you don't need to use the call method here (indeed you almost never
    need to use the call method).

    > I guess I'm going to have to rethink everything from scratch.


    Not at all. You just need to augment all the DIV elements created with
    references to a single function object. You could define that function
    globally:-

    function forDivFun(){
    //do stuff
    }

    - and either directly augment the DIVs following creation:-

    var div = document.createElement('div');
    div.fun = forDivFun;

    - or have another function do the augmentation:-

    function DivAug(div){
    div.fun = forDivFun;
    }

    You probably will not want to (should not want to) define the functions
    that will act as methods of your objects globally, but you have many
    methods of creating such method in a 'private' scope. For example, the
    inline execution of a function expression:-

    var divAug = (function(){
    function forDivFun(){
    //do stuff
    }
    function forDivBar (){
    //do more stuff
    }
    return (function(div){
    div.fun = forDivFun;
    div.bar = forDivBar;
    });
    })();

    The inline execution of the function expression only happens once (as
    the script loads) so the inner functions - forDivFun - and - forDivBar -
    are only created once, but the function returned and assigned to the
    global - divAug - variable is able to assign references to those
    function object instances to all the DIVs passed to it as arguments (-
    divAug(div) -).

    The main drawback with the inline execution of a function expression is
    that you cannot use the - divAug - function until after the source code
    for it has been executed, so the order of the code becomes important
    (rarely a significant problem).

    An alternative 'private' scope strategy is the "Russian doll" pattern
    (named as such because the outer aspect of a function is replaced with
    smaller, more specific, inner aspect during its execution):-

    fucntion divAug(div){
    function forDivFun(){
    //do stuff
    }
    function forDivBar (){
    //do more stuff
    }
    (divAug = (function(div){
    div.fun = forDivFun;
    div.bar = forDivBar;
    }))(div);
    div = null;
    }

    When this function is called for the first time (as - divAug(div); - the
    two inner functions - forDivFun - and - forDivBar - are created and then
    the global - divAug - function is replaced with an inner function that
    has access to - forDivFun - and - forDivBar -. This new function is
    called, passing on the div parameter of the outer function so it may be
    augmented. Finally the - div - parameter is assigned null so that no DOM
    element references are preserved in the closure.

    Following the first call to the above function all subsequent calls
    transparently execute only the inner function that replaced it. (Thus
    the to - forDivFun - and - forDivBar - function objects are only created
    once but may be assigned to all DIVs to be augmented).

    This strategy means that the set-up/configuration of the divAug function
    is delayed until its first use.

    > Thanks for the (albeit disappointing) info.
    >
    > By the way, does this not render object augmentation
    > essentially useless in all but trivial cases?


    Augmentation is completely viable, though augmenting 800+ DOM elements
    seems a bit extreme. But ore information/context would be needed to
    fairly judge the idea.

    Richard.
    Richard Cornford, Jan 27, 2006
    #7
  8. Gawain Lavers wrote:

    > Yeah,


    "Yeah"?

    <URL:http://jibbering.com/faq/faq_notes/pots1.html#ps1Post>
    <URL:http://www.safalra.com/special/googlegroupsreply/>

    > prototype is clearly ideal, but I unfortunately don't have that
    > option -- perhaps a more clear example:
    >
    > function DivAug() {
    > this.fun = function() {
    > //do stuff
    > }
    > }
    >
    > function MyDiv() {
    > var div = document.createElement('div');
    > DivAug.call(div);
    > return div;
    > }
    >
    > The idea I had was that rather than create an external infrastructure
    > which would manage my DOM structure, I would simply augment the DOM
    > elements so that they would know what to do with themselves.
    > Unfortunately, I can't muck with a DOM element's prototype.


    You can in Gecko-based UAs. And in all other UAs, the above is error-prone
    as you are trying to augment a _host object_ [referred to by `div' in
    MyDiv() and `this' in DivAug()]. In ECMAScript implementations, a host
    object may implement the internal [[Put]] method required to add a property
    to an object or change the property value different than specified or not
    at all.

    > [...]
    > By the way, does this not render object augmentation essentially
    > useless in all but trivial cases?


    No, it does not, for appropriate values of "trivial".


    PointedEars
    Thomas 'PointedEars' Lahn, Jan 27, 2006
    #8
  9. Richard Cornford wrote:
    > You probably will not want to (should not want to) define the functions
    > that will act as methods of your objects globally, but you have many
    > methods of creating such method in a 'private' scope. For example, the
    > inline execution of a function expression:-


    And that nails my problem exactly -- I knew full well how to attach a
    function in that manner, but had just screened it out mentally because
    I didn't want to "pollute" the global namespace. In retrospect, it was
    foolish for me to not figure out how call() would behave if I defined
    inner functions in that manner.

    > var divAug = (function(){
    > function forDivFun(){
    > //do stuff
    > }
    > function forDivBar (){
    > //do more stuff
    > }
    > return (function(div){
    > div.fun = forDivFun;
    > div.bar = forDivBar;
    > });
    > })();


    <snip>

    > fucntion divAug(div){
    > function forDivFun(){
    > //do stuff
    > }
    > function forDivBar (){
    > //do more stuff
    > }
    > (divAug = (function(div){
    > div.fun = forDivFun;
    > div.bar = forDivBar;
    > }))(div);
    > div = null;
    > }


    That took me a while to figure out, but that's pretty much exactly what
    I need. Cryptic, but fun. For the moment I've just wrapped the
    methods for my augmenting "classes" in simple objects, but I'll
    probably start trying this out soon. I'm still getting one or two
    warnings on occasion in Firefox, but that's on an underpowered laptop.

    > Augmentation is completely viable, though augmenting 800+ DOM elements
    > seems a bit extreme. But ore information/context would be needed to
    > fairly judge the idea.
    >
    > Richard.


    It's a part of an internal web application where users score observed
    genetic expression from experimental results. The 800+ actually refers
    to a listing of anatomy features (from system to organ to organ
    sub-region to cell type) -- each of these having a number of data
    points that the users need to be able to set -- about 20 or so "active"
    html elements being used to provide pulldown menus, links, and form
    fields for each anatomy feature. The scoring widgets themselves are
    somewhat complex, and choices in one part of the tree can influence
    scores in other parts. Fortunately, as an internal thing, I have a
    certain amount of control over things like browser and cpu, but the key
    was for it to be faster and easier than the previous version, which
    relied entirely on server side scripting and form submissions
    (reloading the page every time a branch was expanded, etc.).

    Anyway, thanks a lot for the help (everyone). Problem solved, and
    rightly or wrongly, I think I understand JavaScript much better now.
    Gawain Lavers, Feb 13, 2006
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Peter L.
    Replies:
    0
    Views:
    436
    Peter L.
    Nov 21, 2004
  2. hvt
    Replies:
    0
    Views:
    1,209
  3. António Marques

    Best route for object augmentation?

    António Marques, Jun 8, 2006, in forum: Javascript
    Replies:
    2
    Views:
    81
    Matt Kruse
    Jun 8, 2006
  4. Daniel
    Replies:
    4
    Views:
    112
    Richard Cornford
    Feb 4, 2007
  5. Peter Michaux
    Replies:
    0
    Views:
    185
    Peter Michaux
    Feb 1, 2007
Loading...

Share This Page