object augmentation and memory usage

G

Gawain Lavers

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.
 
T

Thomas 'PointedEars' Lahn

Gawain said:
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
 
G

Gawain Lavers

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?
 
V

VK

Gawain said:
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.
 
V

VK

Gawain said:
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.
 
R

Richard Cornford

Gawain said:
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.
 
T

Thomas 'PointedEars' Lahn

Gawain said:

"Yeah"?

<URL:http://jibbering.com/faq/faq_notes/pots1.html#ps1Post>
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
 
G

Gawain Lavers

Richard said:
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;
});
})();

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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,816
Latest member
SapanaCarpetStudio

Latest Threads

Top