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.
--
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);
}
}
Garrett
--
comp.lang.javascript FAQ <URL: http://jibbering.com/faq/ >
No.
> it make a difference if FunctionExpression is contained within a
> `return` clause?
What is a return clause?
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?
>
> Garrett
>
> --
> comp.lang.javascript FAQ <URL:http://jibbering.com/faq/>
--
kangax
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.
--
Jorge.
Sorry, I meant to say - return statement.
--
kangax
Yes, it is defined in the specs, which you should be read at least
once.
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.
This isn't just coming from The Good Parts.
Mozilla uses it in their docs, too:
http://www.google.com/search?q=site:developer.mozilla.org+%22function+statement%22
Microsoft use it as well...
http://msdn.microsoft.com/en-us/library/4t2k5yhw.aspx
...as does Flanagan in the "Definitive Guide":
http://preview.tinyurl.com/6yytpv
> 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.
Pessimization :-)
- Conrad
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.
Richard.
Thanks for such thorough explanation.
I'll make sure to read specs better next time.
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.
http://preview.tinyurl.com/mdc-function-expression
(a little further down)
- Conrad
> 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.
/L
--
Lasse Reichstein Holst Nielsen
DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
'Faith without judgement merely degrades the spirit divine.'
They're not ignored. Try the function in my previous post and see if you
get an alert.
> See this thread:
>
> http://groups.google.com/group/comp.lang.javascript/browse_thread/thread/c19e1428f1e1990b
I'm not sure how this is relevant - nothing in that thread suggests that
Mozilla discards function declarations in a block.
- Conrad
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'?
- Conrad
Try this:
<script>
window.onload= function () {
function foo () { alert('outer'); }
if (true) {
function foo () { alert('inner'); }
}
foo();
};
</script>
Mozillas: -> outer
Everywhere else: -> inner
--
Jorge.
Note that changing if (true) to if (false) doesn't make any
difference.
--
Jorge.
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
})();
>
> --
> Jorge.
--
kangax