Path: csiph.com!usenet.pasdenom.info!weretis.net!feeder1.news.weretis.net!news.solani.org!.POSTED!not-for-mail From: Thomas 'PointedEars' Lahn Newsgroups: comp.lang.javascript Subject: Re: Can somebody explain this to me Date: Thu, 08 Jan 2015 22:09 +0100 Organization: PointedEars Software (PES) Lines: 236 Message-ID: <3039223.p41ss7ciAv@PointedEars.de> References: <1590597.KOVAYohEvS@PointedEars.de> <060120151734221023%timstreater@greenbee.net> <1676759.bbBSSFFLiy@PointedEars.de> <66nsaal9kcls42elbn16dkbv3fiille47o@4ax.com> <3105476.iMYZV00USQ@PointedEars.de> Reply-To: Thomas 'PointedEars' Lahn Mime-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8Bit X-Trace: solani.org 1420751380 13117 eJwFwYEBwDAEBMCV+PBiHCX2H6F3fqjsMDrN11fJltURnK92oay5EXJfbwDwbOSLUrTliP4YdxDG (8 Jan 2015 21:09:40 GMT) X-Complaints-To: abuse@news.solani.org NNTP-Posting-Date: Thu, 8 Jan 2015 21:09:40 +0000 (UTC) User-Agent: KNode/4.14.2 X-User-ID: eJwNwoENwDAIA7CXGpHAOIcO+P+ETbbM4W/Q5dT+rO7anQfVjCS96qAVPTDLl63BnAAWkUfhyBG5k4sBP1DFFQQ= Cancel-Lock: sha1:2ti03O8HPrSuDXHPDju94A/ZAWE= X-NNTP-Posting-Host: eJwFwYEBwCAIA7CXVrAFz0GU/09YQhfUsUQtDgctwz7D6hCsnn3BOa/qZJt79oWJsXkhy/wBHtkQzQ== Xref: csiph.com comp.lang.javascript:26505 Christoph M. Becker wrote: > Thomas 'PointedEars' Lahn wrote: >> Christoph M. Becker wrote: >>> A function declaration creates a function object that is passed in the >>> *variable* environment of the running execution context as environment; >>> a function object that is created by a function expression is passed in >>> the *lexical* environment. >> >> Objects are not "passed in environments". > > I had written (simplified): "an object that is passed in something as > environment". The "in" is superfluous or plain wrong -- I'm not sure. If you consult the ECMAScript Language Specification again, you can see that this statement is wrong either way: ,- | | A Lexical Environment is a specification type used to define the | association of /Identifiers/ to specific variables and functions based | upon the lexical nesting structure of ECMAScript code. IOW, Lexical Environments are used for resolving identifiers instead (this is not an ECMAScript-specific idea). Neither are objects "passed in something as environment" nor are objects passed in(to) environments; certainly Function instances (your “function objects”) are not. As for function declarations vs. expressions: | Semantics | | The production | | FunctionDeclaration : function Identifier ( FormalParameterList_opt ) { | FunctionBody } | | is instantiated as follows during Declaration Binding instantiation | (10.5): | | 1. Return the result of creating a new Function object as specified in | [section] 13.2 with parameters specified by FormalParameterList_opt, | and body specified by FunctionBody. | Pass in the VariableEnvironment of the running execution context as ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | the Scope. Pass in true as the Strict flag if the FunctionDeclaration ^^^^^^^^^ | is contained in strict code or if its FunctionBody is strict code. | | | The production | | FunctionExpression : function ( FormalParameterList_opt ) { FunctionBody } | | is evaluated as follows: | | 1. Return the result of creating a new Function object as specified in | [section] 13.2 [“Creating Function Objects”; the ed.] with parameters | specified by FormalParameterList_opt and body specified by | FunctionBody. | Pass in the LexicalEnvironment of the running execution context ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | as the Scope. ^^^^^^^^^^^^^ | Pass in true as the Strict flag if the FunctionExpression is contained | in strict code or if its FunctionBody is strict code. The section referred is | 13.2 Creating Function Objects | | Given an optional parameter list specified by FormalParameterList, a body | specified by FunctionBody, a Lexical Environment specified by Scope, and a | Boolean flag Strict, a Function object is constructed as follows: | | 1. Create a new native ECMAScript object and let F be that object. | […] | 9. Set the [[Scope]] internal property of F to the value of Scope. | […] Continuing in section 11.2.5, you can read: | The production | | FunctionExpression : function Identifier ( FormalParameterListopt ) { | FunctionBody } | | is evaluated as follows: | | 1. Let funcEnv be the result of calling NewDeclarativeEnvironment passing ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | the running execution context’s Lexical Environment as the argument ^^^^^^^^^^^^^^^ The section referred is | 10.2.2.2 NewDeclarativeEnvironment (E) | | When the abstract operation NewDeclarativeEnvironment is called with | either a Lexical Environment or null as argument E the following steps are | performed: | | 1. Let env be a new Lexical Environment. | 2. Let envRec be a new declarative environment record containing no | bindings. | 3. Set env’s environment record to be envRec. | 4. Set the outer lexical environment reference of env to E. | 5. Return env. The text above shows that I have been partially wrong: The “scope rules”, as you put it, *do* differ between function declarations and function expressions; they also differ between function expressions with and without identifier. The subtype, so to speak, of the Function instance’s [[Scope]] property’s value is different in both cases, indeed (its type is *always* Lexical Environment because by definition both LexicalEnvironment and VariableEnvironment are Lexical Environments). This can be explained as follows: The identifier of a function created by function declaration needs to be available both from inside the function and the (outer) execution context in which it was declared. A Function instance created from a function declaration can be accessed and modified from the execution context in which it was declared; the function can also modify itself when it is called¹: you can assign a value to the identifier of the function to do that, but you cannot delete the function, by a “delete” operation on its identifier. IOW, it is treated *like a variable* that is declared in the (outer) execution context in which the function was declared. Also, from the inside identifiers in the outer execution context need to be accessible (closure): /* result should be "answer" per §12.2, but apparently only available internally */ var answer = 42; function foo (bar) { /* 1 */ foo.length; /* false */ delete foo; /* function foo (bar) { … } */ foo; /* function () { … } */ foo = function () { return answer; }; } /* undefined */ foo(); /* 42 */ foo(); foo = function () { return 23; }; /* 23 */ foo(); [This also allows another function declaration with the same identifier to overwrite a previous one in the same execution context.¹] A function created by function expression without identifier is anonymous; without explicit reference, it cannot be referred (or modified) from the inside or the outside², but from the inside identifiers in the outer execution context need to be accessible (closure): /* Script-kiddies, don't do this (at home) */ var counter = 10; (function () { if (counter > 0) { arguments.callee(--counter); } }()); A function expression with identifier must be treated differently than both of those because it must be possible to refer to the resulting Function instance by identifier from within the function (for introspection and recursion), but not from its defining outer execution context. Also, it must still work as a closure. /* see above */ var answer = 42; /* function bar (…) { … } */ var foo = function bar (baz) { /* 1 */ bar.length; /* function () { … } */ bar = function () { return answer; }; }; /* undefined */ foo(); /* undefined */ foo(); foo = function () { return 23; }; /* overwrites/creates “bar” property, overwrites “bar” variable value, or throws “ReferenceError: bar is not defined” in strict mode */ bar = function () { return 11; }; /* 23 */ foo(); ______ ¹ Loosely speaking. What really happens is that the identifier then refers to a new Function instance, and the old instance is free to be garbage- collected unless it is still referred to by something else. In the case of duplicate function declarations in an execution context, an optimizing compiler could skip the creation of all Function instances but the last one. ² Overwriting arguments.callee might effect that anyway, but that would not be conforming behavior (overwriting a property ought not change the object referred to by its value). > Thanks for the clarifications. :) You're welcome. -- PointedEars FAQ: | SVN: Twitter: @PointedEars2 | ES Matrix: Please do not cc me. / Bitte keine Kopien per E-Mail.