Today I saw a magic question about a function that has a default value that forms its own scope
var x = 1;
function f(x, y = function () { x = 3; console.log(x); }) {
console.log(x)
var x = 2
y()
console.log(x)
}
f()
console.log(x)
Copy the code
The correct answer is undefined, 3, 2, 1
Why does this answer come up?
In section 9.2.12 of the ECMAScript2015 document, we find the following sentence:
When an execution context is established for evaluating an ECMAScript function a new function Environment Record is created and bindings for each formal parameter are instantiated in that Environment Record. Each declaration in the Function body is also instantiated. If the function’s formal parameters do not include any default value initializers then the body declarations are instantiated in the same Environment Record as the parameters. If default value parameter initializers exist, a second Environment Record is created for the body declarations. Formal parameters and functions are initialized as Part of FunctionDeclarationInstantiation. All other bindings are initialized during evaluation of the function body.”
If the parameters of a function do not have default values, the declaration inside the function body will run in the same environment as the declaration of the parameters of the function. If the function has default values, there will be a second environment for the declaration inside the function body.
Ruan Yifeng in ES6 standard introduction, function expansion chapter has similar words:
Once the default values of parameters are set, the parameters form a separate context when the function is declared initialized. After initialization, the scope will disappear. This syntactic behavior does not occur if the parameter defaults are not set. “
Let’s look at the order of execution based on this reference.
Var x = 1; A variable x = 1 is created in the global scope
Then execute the f () function calls into the f function, because the function is the default value, then the f function has three scope, is the top layer of function scope, then the function of outer scope as well as the global scope, because of the function body var statement, there is a variable x, because the default value is not an assignment, The first console.log(x) outputs the x value of the inner scope of the nearest function, which is undefined.
Var x = 2; var x = 2; var x = 2; var x = 2;
When y function is generated, there is a reference to the external variable x, so there is a closure {x = undefined}. X of this closure points to the outer scope of f function.
Executive function y, y function execution context there are three layers of scope, the first layer is a function of the outer scope, the scope of the second layer is formed a closure, the closure to the f function function of the outer scope, the third layer is the global scope, is in the process of executive function y, x = 3, then x cannot be found within the current execution environment, X is found in the closure scope. This x points to the outer function scope of f function, that is, x in the closure is changed to 3, and x in the outer function scope of F function will also be changed to 3. Then console.log(x) looks in scope and finds x = 3 in the closure, which prints 3.
Log (x), x first searches the current scope (function inner scope), finds x = 2, and prints 2.
Log (x) is executed. If x = 1, the last console.log(x) is executed.
Since want to explore certainly can’t just ended, we change the var x = 2 to let x = 2, found the console error “Identifier ‘x’ has already had been declared”, should be because of this reason, let not repeated statements, This also indicates that the declaration of a function parameter is executed once in the inner scope of the function. With this in mind, I have an idea to give the default value 0 for parameter x, and the code changes to:
var x = 1;
function f(x = 0,y = function () { x = 3; console.log(x); }) {
console.log(x)
var x = 2
y()
console.log(x)
}
f()
console.log(x)
Copy the code
The function of the final output is 0, 1, 3, 2, according to the above theory, because of the var x = 2, will be in the inner function scope, to create a variable x, and the function of outer scope will there is a variable x, initialization function is created, will make the functions the inner and outer two layers within the scope of variables are initialized to 0 x, The first console.log(x) will print x = 0 in the function’s inner layer, or 0.
And then var x is equal to 2; It changes the x of the inner layer of the function to 2, that is, x =2, and the x of the outer scope of the function is still x = 0.
If y() is encountered, x = 3 in y, so that x in the outer scope of the function is 0, console.log(x) prints 3.
If I continue, console.log(x) will output x in the function’s inner scope and output 2
Exit f and execute console.log(x). The output is x for the global environment, that is, 1.
For further verification, I made further changes to the code. We do not declare the function inside the function body, so the code is changed to
var x = 1;
function f(x = 0,y = function () { x = 3; console.log(x); }) {
console.log(x)
x = 2
y()
console.log(x)
}
f()
console.log(x)
Copy the code
We first declare a variable x = 1 in the global scope, and then execute f().
Initialized into the f function, first of all, because of the function body function declarations, there are no inner function scope and outer scope, that is to say at this time only two function in environmental scope, namely, the scope of the function itself as well as the global scope, the same y function in the initialization closures, also in the process of the closure point f function scope.
Execute the first console.log(x), looking for the x value in the adjacent scope, that is, finding the initialized x = 0 in the function scope, and output 0.
Continue to execute x = 2, at which point change the scope of the function to x 2.
Then execute (y), enter y function implementation, executed first is x = 3, in the scope of no x y function value, to the upper search, the upper scope is the function of form closure, the closure point is a function of f function scope of x, then modify the f function function scope for 3 x, then the console. The log (x), The output is 3.
F continues to execute, and console.log(x) is encountered. Since y has changed the value of x in f’s scope during execution, the output is 3.
Log (x). If x = 1 in the global scope is output, the output is 1.
What if there is no default value?
var x = 1;
function f(y,x) {
console.log(x)
var x = 2
y()
console.log(x)
}
f(function () { x = 3; console.log(x); })
console.log(x)
Copy the code
What happens when you make some changes to the code and the function parameters do not have default values?
Let’s start with var x = 1 on the first line; Creates a variable x = 1 in the global scope;
F (function () {x = 3; console.log(x); }); When y is assigned to a function, the scope of the function is observed, and it is found that there are still only two scopes, then the function f is entered
The first console.log(x) is encountered; If x is not assigned and the value is undefined, undefined is output.
We then execute var x = 2 to assign x in scope to 2
Then perform function y, y function execution is very amazing, because the y in the initialization process is not a function, then this function does not produce closures, the function is only the scope of the function itself y y scope as well as the global scope, the performing x = 3, in the function scope to find x, at global scope search, find x, And change it to 3. Again, the following console.log(x) value will print the global scope x, which is 3.
It then executes console.log(x) in f, and outputs x in the function’s scope, which is 2.
Exit f and execute console.log(x), which prints global scope x, also 3.