JS预解析时变量提升、函数提升、函数实参传值的先后问题
一、全局作用域内
在全局作用域内有这样几句代码,我们来思考一下其执行结果。
<script>
console.log(f);
function f() {};
var f = 3;
</script>
代码的顺序是先函数声明后变量声明,如果函数提升和变量提升的优先级一样,相当于执行了如下代码:
var f = function f() {};
var f = undefined;
console.log(f); //结果为undefined
f = 3;
如果函数提升在变量提升之前,相当于执行:
var f = function f() {};
var f = undefined;
console.log(f); //结果为undefined
f = 3;
如果函数提升在变量提升之后,相当于执行:
var f = undefined;
var f = function f() {};
console.log(f); //结果为function f() {}
f = 3;
而实际上的输出结果为:function f() {}
这说明了:在预解析时,函数提升是在变量提升之后,函数提升的值会覆盖之前的变量提升的值。
二、函数作用域内
- 第一段代码:
<script>
function f(a) {
console.log(a);
var a = 2;
}
f(1);
</script>
其输出结果为传入的实参的值1,这个答案貌似显而易见,不过这其实说明了一个信息:函数内,变量提升在前,实参赋值在后。
上面代码等价于:
<script>
function f(a) {
var a = undefined;
a = 1;
console.log(a);
a = 2;
}
f(1);
</script>
- 第二段代码
<script>
function f(a) {
console.log(a);
function a() {};
}
f(1);
</script>
其输出结果为:function a() {},这说明:实参赋值在前,函数提升在后。
所以能得出结论:函数内的预解析流程是,先变量提升,再进行实参给形参赋值,最后函数提升,后面的值会覆盖前面的值。
测试题(来自于B站UP主:程序员有道)
function fn(a, c) {
console.log(a);
var a = 123;
console.log(a);
console.log(c);
function a() {};
if (false) {
var d = 678;
};
console.log(d);
console.log(b);
var b = function () {};
console.log(b);
function c() {};
console.log(c);
}
fn(1, 2);
分析:
函数调用后,在开始执行函数内的代码前会进行预解析,函数内的预解析流程是:1. 变量提升;2. 实参给形参赋值;3. 函数提升。
现在就以这个流程来分析这道题。
- 变量提升阶段(有声明变量的地方就会有变量提升)
var a = undefined; //var a = 123;
var b = undefined; //var b = function () {};为变量声明,非函数声明
var d = undefined; //var d = 678;
- 实参给形参赋值
a = 1;
var c = 2;
- 函数提升
a = function a() {}; //function a() {};
c = function c() {}; //function c() {};
所以预解析后的结果为:
a = function a() {};
b = undefined;
c = function c() {};
d = undefined;
所以该测试代码等价于:
var a = function a() {};
var b = undefined;
var c = function c() {};
var d = undefined;
console.log(a); //输出function a() {}
a = 123;
console.log(a); //输出123
console.log(c); //输出function c() {}
if (false) {
d = 678;
};
console.log(d); //输出undefined
console.log(b); //输出undefined
b = function () {};
console.log(b); //输出function () {}
console.log(c); //输出function c() {}