FunctionExpression's and memory consumptions

K

kangax

There's a common pattern of "forking" a returning function as in the
following example:

function bind(fn, context) {
var args = Array.prototype.slice.call(arguments, 2);
if (args.length) {
return function() {
fn.apply(context, args);
}
}
return function() {
return fn.call(context);
}
}

The runtime speed benefits are obvious, but I've been told that there
is an increased memory consumption in such cases. Are there 2 Function
objects created when `bind` is being called? I assume that's not the
case, since those are not FunctionDeclaration's, but rather
FunctionExpression's (and so they should not be evaluated foremost
when execution context is entered). Are FunctionExpression's contained
within blocks that are never evaluated create Function objects? Does
it make a difference if FunctionExpression is contained within a
`return` clause?

I can't find relevant parts in the specification and would appreciate
any insights on this matter.
 
D

dhtml

kangax said:
There's a common pattern of "forking" a returning function as in the
following example:

function bind(fn, context) {
var args = Array.prototype.slice.call(arguments, 2);
if (args.length) {
return function() {
fn.apply(context, args);
}
}
return function() {
return fn.call(context);
}
}

The runtime speed benefits are obvious, but I've been told that there
is an increased memory consumption in such cases. Are there 2 Function
objects created when `bind` is being called? I assume that's not the
case, since those are not FunctionDeclaration's, but rather
FunctionExpression's (and so they should not be evaluated foremost
when execution context is entered). Are FunctionExpression's contained
within blocks that are never evaluated create Function objects? Does
it make a difference if FunctionExpression is contained within a
`return` clause?

Unreached expressions should have no effect. We can see a call expression:-

function unreachableExpression() {
if(true) return;
alert("Panic!!! I did not feature-test alert!");
}

And a return statement would also have no effect:-

function unreachableStatement() {
if(true) return true;
return false;
}

Each time that bind function is called, a new function is created, not
two. If arguments.length were 0, the second function expression would be
returned, but would always err because fn would be undefined. You
probably meant to check "if(arguments.length > 2)".

function f(){
alert(this.type);
}

var e = bind(f, { sound: "elephant" });

The second function does not need the args property, so creating an
array would be unnecessary.

function bind(fn, context) {
var args;
if (arguments.length > 2) {
args = Array.prototype.slice.call(arguments, 2);
return function() {
fn.apply(context, args);
}
}
return function() {
return fn.call(context);
}
}

Garrett
 
D

David Mark

There's a common pattern of "forking" a returning function as in the
following example:

function bind(fn, context) {
  var args = Array.prototype.slice.call(arguments, 2);
  if (args.length) {
    return function() {
      fn.apply(context, args);
    }
  }
  return function() {
    return fn.call(context);
  }

}

The runtime speed benefits are obvious, but I've been told that there
is an increased memory consumption in such cases. Are there 2 Function
objects created when `bind` is being called? I assume that's not the
case, since those are not FunctionDeclaration's, but rather
FunctionExpression's (and so they should not be evaluated foremost
when execution context is entered). Are FunctionExpression's contained
within blocks that are never evaluated create Function objects? Does
No.

it make a difference if FunctionExpression is contained within a
`return` clause?

What is a return clause?
 
K

kangax

Unreached expressions should have no effect. We can see a call expression:-

function unreachableExpression() {
if(true) return;
alert("Panic!!! I did not feature-test alert!");

}

And a return statement would also have no effect:-

function unreachableStatement() {
if(true) return true;
return false;

}

Each time that bind function is called, a new function is created, not
two. If arguments.length were 0, the second function expression would be
returned, but would always err because fn would be undefined. You
probably meant to check "if(arguments.length > 2)".

function f(){
alert(this.type);

}

var e = bind(f, { sound: "elephant" });

The second function does not need the args property, so creating an
array would be unnecessary.

function bind(fn, context) {
var args;
if (arguments.length > 2) {
args = Array.prototype.slice.call(arguments, 2);
return function() {
fn.apply(context, args);
}
}
return function() {
return fn.call(context);
}

}

Thanks, Garrett.
That makes much sense.

So, as far as I understand, function expression in "unreached" block
is no different than any other expression in "unreached" block (in a
sense that it shouldn't be executed and so shouldn't allocate any
memory). Does spec actually define such behavior (object
initialization) or is it left up to an implementation?
 
J

Jorge

There's a common pattern of "forking" a returning function as in the
following example:

function bind(fn, context) {
  var args = Array.prototype.slice.call(arguments, 2);
  if (args.length) {
    return function() {
      fn.apply(context, args);
    }
  }
  return function() {
    return fn.call(context);
  }

}

The runtime speed benefits are obvious, but I've been told that there
is an increased memory consumption in such cases. Are there 2 Function
objects created when `bind` is being called? I assume that's not the
case, since those are not FunctionDeclaration's, but rather
FunctionExpression's (and so they should not be evaluated foremost
when execution context is entered). Are FunctionExpression's contained
within blocks that are never evaluated create Function objects? Does
it make a difference if FunctionExpression is contained within a
`return` clause?

I can't find relevant parts in the specification and would appreciate
any insights on this matter.

Note that that creates both a function and a closure: the context of
"bind" that is saved in the closure occupies memory as well. But only
the function that is returned is created.
 
D

David Mark

Thanks, Garrett.
That makes much sense.

So, as far as I understand, function expression in "unreached" block
is no different than any other expression in "unreached" block (in a
sense that it shouldn't be executed and so shouldn't allocate any
memory). Does spec actually define such behavior (object
initialization) or is it left up to an implementation?

Yes, it is defined in the specs, which you should be read at least
once.
 
D

dhtml

kangax said:
Thanks, Garrett.
That makes much sense.

A FunctionExpression is an Expression. See 11.2.5 Function Expressions.

A return statement is statement. If it is not reached, it is not
executed. I'm sure you already know this, but seem to be concerned that
a function expression might be initialized in memory when a context is
entered. It isn't. Expressions are part of statements.


| A.4 Statements
| Statement :See clause 12
| Block
| VariableStatement
| EmptyStatement
| ExpressionStatement
| IfStatement
| IterationStatement
| ContinueStatement
| BreakStatement
| ReturnStatement
| WithStatement
| LabelledStatement
| SwitchStatement
| ThrowStatement
| TryStatement


I see that "The Good Parts" calls "FunctionDeclaration" a "function
Statement". This is completely misleading and I've been seeing posts
using this wrong terminology a lot lately.

The Good Parts, pg 113:

The function Statement vs the function Expression.

This is wrong terminology. What he is calling a function Statement is
really a FunctionDeclaration. A FunctionDeclaration is not a Statement.
It cannot appear where statements do.

What's worse: Some implementations do have an extension of function
statement, so the term "function statement" as Doug uses it, is wrong.

The explanation given:

| The statement:
|
| function foo(){}
|
| means about the same thing as:
|
| var foo = function foo(){};
|

The first code example is not a statement. It's a function declaration.
It is is evaluated before all statements, so it can be referred to, as in:

foo();
function foo() { alert('foo stuff'); }

elerts 'foo stuff'

whereas:

bar();
var bar = function bar(){ alert('bar stuff'); };

results in a TypeError from trying to call something that is not a
function (bar is undefined).

The book mentions implementation problems with RegExps. If it is going
to mention problems with implementations, it should be much more
thorough about it. In particular, the form of using an identifier in a
FunctionExpression results in JScript interpreting a function expression
and a function declaration which applies to the example above.

But I digressed heavily. Back to your question, a FunctionExpression is
an an expression and an Expression is not a function declaration. Here's
a final example:-

if(true) {
(function() { alert(true); })();
} else {
(function() { alert(false); })();
}

I think you can guess which one it will get to. The FunctionExpression
is not evaluated when the execution context is entered and it is not
evaluated as a statement (not reached). An implementation that evaluated
the function on either pass would seem to be doing whatever is the
opposite of an optimization.

Now a FunctionDeclaration, OTOH, is not an Expression. For that, you can
see 10.1.3 Variable Instantiation:
| * For each FunctionDeclaration in the code, in source text
| order, create a property of the variable object whose name is the
| Identifier in the FunctionDeclaration, whose value is the result
| returned by creating a Function object as described in 13, and whose
| attributes are determined by the type of code. If the variable object
| already has a property with this name, replace its value and
| attributes. Semantically, this step must follow the creation of
| FormalParameterList properties.

http://bclary.com/2004/11/07/#a-10.1.3
So, as far as I understand, function expression in "unreached" block
is no different than any other expression in "unreached" block (in a
sense that it shouldn't be executed and so shouldn't allocate any
memory). Does spec actually define such behavior (object
initialization) or is it left up to an implementation?

If the returned function is saved to a variable, then it stays in
memory. However, this would be true regardless of if/else, as there are
no known implementations that join functions.

We can see "Entering an Execution Context", section 10.2
http://bclary.com/2004/11/07/#a-10.2

No mention of allocating memory unreachable blocks.

the "if" statement is also relevant. section 12.5.
 
R

Richard Cornford

This isn't just coming from The Good Parts.
Mozilla uses it in their docs, too:
Microsoft use it as well...
...as does Flanagan in the "Definitive Guide":
<snip>

Poor terminology choices are normal for both Flanagan and Microsoft,
and Mozilla documentation is still a little short of being good
(though certainly much improved over recent years). However, much
writing on the subject of javascript is (where it is not actually
inaccurate) aimed at audiences who have a great deal to learn before
they appreciate the subtleties; where moving in the direction of
understanding might be seen more productive than laying out the gory
details in full.

Most current implementations allow nesting:

if (blah) {
function blimm () {
....
}

}

Mozilla treats this as a function expression,

Not it does not.
not a function declaration, meaning that it will not be
evaluated unless blah is true.

With a function expression with optional Identifier (which is what the
above would be if it were a function expression) the resulting
function object can only be referenced using that Identifier from
inside the body of the function (all else being equal), because an new
object is added to the new function object's [[Scope]] and the
Identifier used to create a named property of that object to refer to
the function. Thus - blimm - is out of scope in the surrounding code,
if the code were a function expression.

In Mozilla browsers the function does become available using the
Identifier - blimm - in the containing scope, thus this is not a
function expression. In fact it is a function statement; a syntax
extension that is capable of allowing the conditional creation of a
function because being a Statement it can be evaluative inside a
block.

Mozilla also has FunctionDeclarations (which create function objects
during variable instantiation) and FunctionExpressions (which create
function object when evaluated, and do follow the ECMAScript rules
regarding optional Identifiers). The Mozilla docs might blur the
distinction but it exists regardless.

IE browsers also process the above code successfully (or at lest
without erroring) , but they see the function as a FunctionDeclaration
regardless of its 'illegal' context. Thus IE would create a function
object during variable instantiation, and so any surrounding
conditions (the - if(blah){ ... } - in this case) become redundant,
and the last FunctionDeclaration with any given name becomes the only
one available in the scope.

Most browsers (more or less) imitate one of these 'extensions' for
reasons of compatibility. But notations of which they should be
compatible with (JScript or JavaScript(tm)) seem to change. As I
recall (and my memory may not be accurate on this point) Opera had
switched from following JScript to following JavaScript, which Safari
has switched from following JavaScript to following JScript.

Obviously with two distinct interpretations of this code structure,
and minor browsers not necessarily sticking to following one or the
other (plus some related bugs that introduce some more variation)
using this type of code structure is seriously ill-advised. With
results that that may appear to 'work', but maybe only by coincidence
and for the time being. Plus (and most importantly) conditional
creation of function objects is (and always has been) possible using
pure ECMAScript constructs (by assigning the results of evaluating
function expressions to variables declared in the containing scope).

Interestingly the draft ES 3.1 spec is trying to switch
FunctionDeclaration to being a Statement. How that will work out
remains to be seen as the last draft I looked at (the one before the
current draft, which I haven't looked at yet) did not include any
processing or evaluation rules for their function statements (a bit of
an oversight as it makes the draft language non-viable as it stood).
Only source elements will be treated as function declarations.
<snip>

Only function declarations will be treated as function declarations
(in ES 3), Statements are SourceElements.

Richard.
 
K

kangax

[snip explanation]

Thanks for such thorough explanation.
I'll make sure to read specs better next time.
 
J

Jorge

You're right, what I meant to say was Mozilla "treats it as if it were
a function expression of the form: var blimm = function () {...}".
Although it has the form of a function declaration, no function object
will be created during the variable instantiation phase. Only when the
function "statement" is evaluated will its identifier become available
in the current scope.

No no it's not so. Any function declaration inside an inner block is
considered an error and ignored (in Mozillas). See this thread:

http://groups.google.com/group/comp.lang.javascript/browse_thread/thread/c19e1428f1e1990b
 
J

Jorge

They're not ignored. Try the function in my previous post and see if you
get an alert.
(...)
I'm not sure how this is relevant - nothing in that thread suggests that
Mozilla discards function declarations in a block.

Try this:

<script>
window.onload= function () {
function foo () { alert('outer'); }
if (true) {
function foo () { alert('inner'); }
}
foo();
};
</script>

Mozillas: -> outer
Everywhere else: -> inner
 
J

Jorge

Try this:

<script>
window.onload= function () {
  function foo () { alert('outer'); }
  if (true) {
    function foo () { alert('inner'); }
  }
  foo();};

</script>

Mozillas: -> outer
Everywhere else: -> inner

Note that changing if (true) to if (false) doesn't make any
difference.
 
K

kangax

Note that changing if (true) to if (false) doesn't make any
difference.

It's interesting to observe FunctionStatement's (if we can call it
that way) behavior. From what I can see:

1) FunctionStatement's do not overwrite variables declared via
FunctionDeclaration's
2) FunctionStatement's are not declared when enclosing block is being
entered (but only when a statement is being evaluated)
3) Once declared, FunctionStatement's are available to the entire
function scope (enclosing one), even outside of an actual block (in
which it was evaluated).
4) One FunctionStatement can overwrite another FunctionStatement with
the same identifier.

Tested in: Firefox 3.0.3; Mac OS X 10.5

function write(s) {
document.write(s + '<br>');
}

(function(){

function f(){ return 'outer' };

if (true) {
write(
'typeof g (before FunctionStatement) ' +
(typeof g)
); // undefined

function g(){ return 'inner' };

write(
'typeof g (after FunctionStatement) ' +
(typeof g)
); // function

write('g() ' + g()); // inner

write('delete g ' + (delete g)); // false
write(
'typeof g (after deletion) ' +
(typeof g)
); // function

function g(){ return 'inner2' }
write(
'g() (second FunctionStatement with the same identifier) ' +
g()
); // inner2

function f(){ return 'inner' }
write(
'f() (after FunctionStatement named as '+
'FunctionDeclaration outside of enclosing block) ' +
f()
); // outer
}

write(
'typeof g (outside of block in which it was declared) ' +
(typeof g)
); // function
write(
'g() (outside of block in which it was declared) ' +
g()
); // inner2

})();
 

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,997
Messages
2,570,239
Members
46,827
Latest member
DMUK_Beginner

Latest Threads

Top