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.
> 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); }
> 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?
> > 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:-
> 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.
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?
> 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.
On Oct 28, 12:25 am, David Mark <dmark.cins...@gmail.com> wrote:
> On Oct 26, 9:42 pm, kangax <kan...@gmail.com> wrote: [snip] > > it make a difference if FunctionExpression is contained within a > > `return` clause?
> > > 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:-
> > 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.
> 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.
kangax wrote: > On Oct 26, 10:49 pm, dhtml <dhtmlkitc...@gmail.com> wrote: >> kangax wrote: >>> 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:-
>> 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.
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.
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.
> 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.
> 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.
> 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.
Most current implementations allow nesting:
if (blah) { function blimm () { .... }
}
Mozilla treats this as a function expression, not a function declaration, meaning that it will not be evaluated unless blah is true. Only source elements will be treated as function declarations.
> An implementation that evaluated the function on either pass would > seem to be doing whatever is the opposite of an optimization.
>> 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.
> This isn't just coming from The Good Parts. > Mozilla uses it in their docs, too: <snip> > Microsoft use it as well... <snip> > ...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.
<snip>
> 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.
>> if (blah) { >> function blimm () { >> .... >> }
>> }
>> Mozilla treats this as a function expression,
> Not it does not. [..] > 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.
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.
Should the term "function statement" be used to specifically refer to this extension?
> 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. [...] > 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.
It appears that only Mozilla doesn't see them as function declarations. In this example:
function testFD() { if (true) { function blimm() { alert("true"); } } else { function blimm() { alert("false"); } } blimm();
}
Opera 9.61 alerts "false", same as IE and Safari. Konqueror (old 3.5.2 version) doesn't see blimm at all.
> [..] using this type of code structure is seriously ill-advised.
Of course; I fully agree.
> Interestingly the draft ES 3.1 spec is trying to switch > FunctionDeclaration to being a Statement.
I didn't know that. Could make things... interesting.
>> 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.
Not all Statements are SourceElements. Mozilla will not treat a "function declaration in a block" as a FunctionDeclaration, because it's nested, and thus not a SourceElement.
Conrad Lender <crlen...@yahoo.com> writes: > 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 () {...}".
Not exectly.
> 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.
Exactly. A "var" declaration would also declare the "blimm" variable globally in the scope. As you say, that's not what happens. Mozilla only declares blimm if you actually execute the function statement. It really is different from both "var" and "function" declarations, since it adds a variable to the scope AFTER it was initially created and populated by the declarations.
On 2008-10-31 06:55, Lasse Reichstein Nielsen wrote:
>> 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.
> Exactly. A "var" declaration would also declare the "blimm" variable > globally in the scope. As you say, that's not what happens. Mozilla > only declares blimm if you actually execute the function statement. It > really is different from both "var" and "function" declarations, since > it adds a variable to the scope AFTER it was initially created and > populated by the declarations.
The difference between "var x = function ..." and "function x ..." can be shown in an example:
function testFD1() { print(delete blimm); // =true (blimm not declared) if (1) { function blimm() { alert("true"); } }
}
function testFD2() { print(delete blimm); // =false (blimm has DontDelete attribute) if (1) { var blimm = function() { alert("true"); } }
}
Out of curiosity, does this difference have any practical consequences other than the return value of 'delete'?
On Nov 1, 2:24 am, Conrad Lender <crlen...@yahoo.com> wrote:
> On 2008-10-31 08:21, Jorge wrote:
> > No no it's not so. Any function declaration inside an inner block is > > considered an error and ignored (in Mozillas).
> 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();
> On Nov 1, 2:24 am, Conrad Lender <crlen...@yahoo.com> wrote:
> > On 2008-10-31 08:21, Jorge wrote:
> > > No no it's not so. Any function declaration inside an inner block is > > > considered an error and ignored (in Mozillas).
> > 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
Note that changing if (true) to if (false) doesn't make any difference.
> 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