JS课程学习之预处理与执行

988 阅读3分钟

全局预处理与执行

js解析代码时,并不是读一行处理一行的,而是会有一个预处理阶段。

预处理阶段,只要满足以下两个条件:

  • 用声明的方式创建的函数,即如下形式:
    function xxx(){
    
    }
  • 用var定义的变量

js就会将查找到的函数或者变量名放到词法环境中去,如下

lexicalEnvironment{
    变量名:undefined;
    函数名:对函数的一个引用
}

比如下列代码

var a=5;
function xxx(){
    
}

此时创建的词法环境为

lexicalEnvironment{
    a:undefined;
    xxx:保存对xxx函数的引用
}

如果没有用var定义,而是全局变量的话,并不会加到词法环境中去。如下:

console.log(a); //undefined
console.log(b); //报错:b is not defined

var a=5;
b=6;

其实此时的a输出undefined,也就是我们平常所说的var定义的变量会存在“变量提升,赋值不提升”了。

在代码的执行阶段,执行原则如下:

  • 代码会从上往下执行
  • 遇到存在于词法环境中的变量时进行赋值
  • 遇到不存在词法环境中的变量时,js将一次性执行声明,即让变量成为词法作用域的成员并进行赋值操作。一步到位~

小测试来啦,请分析以下代码的输出:

alert(a);
alert(b);
alert(f);
alert(g);

var a=5;
b=6;
alert(b);
function f() {
    console.log('f');
}
var g=function () {
  console.log('g');
}
alert(g);

当当,正确输出结果是:

  • undefined
  • 报错:b is not undefined(此时一旦报错后面代码就不执行了,所以可将上述程序代码中 alert (b)注释掉 再查看后续结果)
  • 以字符串的形式显示的f函数
  • undefined 因为g是以函数表达式的方法进行定义的,预处理时不会添加到词法作用域里
  • 6 //即第二个alert (b)的结果
  • 以字符串的形式显示的g函数,即第二个alert(g)的结果

个人小总结:变量没添加到词法作用域时,会报错;而函数没添加到词法作用域时,会undefined;

函数预处理与执行

函数是可以调用多次的,每调用一次,会产生一个词法环境对象,全局预处理中这个环境对象指的就是window对象,在函数预处理中,该对象不可见,无法访问,是解析器的东西。

如果函数中有两个参数,但是调用时只传入了一个,即

function aaaa(a,b){
    
}

aaaa(1)

此时的词法环境对象为

leEnvironment{
a:1
b:undefined
}

如果函数中有两个参数,调用时也传入了两个,但是都有变量冲突:

function f(a,b) {
    alert(a);  
    alert(b);

    var b = 100;
    function a() {
    
    }
}
f(1,2);  //函数调用传入了两个参数

此时传入的参数a=1和function a的函数名a冲突; 调用时传入的参数b=2和内部定义的b=100冲突

正确输出为:

  • 2

即传入参数与内部函数名冲突时,函数是一等功名;而传入参数和内部自定义参数冲突时,使用的仍是调用时传入的参数值。