Variable/Lexical environments

后端 未结 1 2079
无人共我
无人共我 2021-01-07 05:53

As said in sec 10.4.3

The following steps are performed when control enters the execution context for function code contained in function object F,

1条回答
  •  有刺的猬
    2021-01-07 06:00

    I am not sure I understand your question, but this is how I understand sec 10.4.3 :

    steps 1 to 4 are dealing with the value of this. Basically, in strict mode, this will be left to a null or undefined value instead of defaulting to the global object (window in case of a browser). This covers the cases when a function is not invoked through usual object or event handler mechanisms.

    step 5 to 7 mean that a new naming environment is created each time you enter a function. It describes the creation of this environment, which is linked to the previous one to form the current name scope.

    For each new function, two environments are co-existing. When a name is resolved, Lexical environment is searched first, then Variable environment. If both searches fail, the process is repeated at the upper level of the environment chain, until the 'catch-all' global scope is encountered. In this scope, all identifiers are handled as properties of the global (window) object. You can picture it as the whole code being enclosed in a with (window) block.

    Lexical environment can be seen as a temporary augmentation of the variable scope. Lexical and Variable environments are functionnally identical until you alter the lexical environment with two specific statements: with or catch. It does not mean they are implemented as identical data structures.

    In terms of implementation, you could imagine the lexical environment as an empty list and the variable environment as a list containing all local variable and parameter names. When a catch or with statement is encountered, the lexical list is populated with new names that will take precedence over the ones stored in the variable list.

    catch will simply make its argument available for name resolution (i.e. allow you to reference the exception parameter). No big deal since the new name is just as explicit as a function parameter.

    with is a rather more dangerous beast. It will create a new environment with the names of all properties of the object passed as its argument. The scope will consist of the chain of variable environments plus this new lexical environment. Here the new names available for resolution are 'hidden' inside the object. For instance:

    var a = 'a', b = 'surprise!', o = {a:'a'};
    with (o) { a = b; }
    console.log (a+" "+b+" "+o.a);
    

    will yield

    a surprise! surprise!
    

    a is resolved as o.a since o contains a property named a. b is not found inside the lexical environment and thus the current variable environment is tried and variable 'b' is found. This is a pretty dangerous mechanism, because if you believe an object contains a given property while it actually doesn't, you will instead reference a variable outside the current scope. For instance, a simple typo like this:

    with (element.style) {leftt = '10px';}
    

    will set the window.leftt property to '10px', unless you happen to have declared a variable named leftt somewhere in the current scope.

    Now if you give silly names like 'i' or 'j' to your object properties, chances are you will clobber a random loop index somewhere up the scope chain while believing you are setting the property of an object.

    step 8 describes the parameters binding once the function scope is established. Basically, the parameters are bound with the values and their names are added to the variable environment.

    The whole point of keeping two separate environments is that the hoisting mechanism always uses the variable environment chain as a scope.

    The idea is that a variable or function should behave as if it had been declared at the top of the current scope block, so for instance a function declared inside a with block should not resolve its names with the object properties exposed by the with statement.

    Frankly this is a rather moot point, since ECMA spec does not allow function declarations inside blocks, although most implementations do, with varrying results.

    Now for your example:

    function foo(){
        var a={p:'p'};
        o={c:'c'};
    }
    

    Your function does not contain any with or catch statements, so the scope chain inside 'foo()' is just a list of two variable environments :

    global (a bunch of DOM objects all seen as properties of 'window')
        function foo (var a)
    

    once you call foo(),

    • a will resolve as foo's local variable, will be created as an object with the property p of value 'p' (and garbage collected as soon as you leave foo(), unless you manage to reference it from a persistent variable).

    • o will not be found in foo's variable environment, so it will be caught by the 'catch-all' global scope and thus resolved as a (new) property of the window object. It will create a window.o.c property with the value 'c'.

    Does that answer somewhat to your question?

    0 讨论(0)
提交回复
热议问题