14

JS运行阶段

JavaScript的运行阶段分为预编译阶段执行阶段,今天要讨论的变量声明提升和函数声明提升,就是在这个阶段完成的。

在预编译阶段,JS引擎会做一件事情,那就是读取变量的定义确定其作用域即生效范围。

  • 变量定义

    • 使用var关键字定义的变量,并未赋值的情况下,该变量的值是undefined
  • 变量作用域

    • 全局变量的作用域遍布全局
    • 局部变量的作用域仅在于函数内部
    • 函数内部的同名变量或参数其优先级高于全局同名变量

变量声明提升

先看一个简单示例:

var name = 'ryan';

function say(){
  console.log(name); //输出:undefined
  var name = 'zoe';
  console.log(name); //输出:'zoe'
}

say();

解析:上述代码从结果看,say函数执行第一次打印name时,并未打印全局的name('ryan'),而是打印局部的name(undefined),这是因为在预编译阶段,say函数内部进行了变量声明提升,提升后的执行效果如下:

var name = 'ryan';

function say(){
  var name; //变量name声明提升至作用域顶部,但未赋值,故为undefined
  console.log(name); //存在局部name,则无视全局name
  name = 'zoe'; //变量赋值保持原位
  console.log(name); //输出:'zoe'
}

say();

函数声明提升

函数的两种创建方式:

  • 函数声明
  • 函数表达式

函数声明:

say(); //输出:'saying'

function say(){
  console.log('saying');
}

函数表达式:

say(); //报错:say is not a function

var say = function(){
  console.log('saying');
}

解析:同样地先执行函数,后创建函数,结果却是不一样。原因在于,通过函数声明的方式,该函数声明(包括定义)会被提升至作用域的顶部,而表达式的创建方式则只提升了变量say至作用域的顶部,此时的say其值为undefined,调用say()自然报错“say不是一个方法”。

再来看一个示例:

var say = function(){
  console.log('1');
};

function say(){
  console.log('2');
};

say(); //输出:'1'

解析:预编译阶段进行变量声明提升和函数声明提升后,上述代码执行效果等同于:

var say; //变量声明提升

function say(){ //函数声明提升
  console.log('2');
}

say = function(){ //变量赋值保持原位执行,say函数被覆盖
  console.log('1');
};

say(); //输出'1'

总结:函数声明提升,会将函数的声明和定义全都提升至作用域顶部。
变量声明提升,只提升声明部分(未赋值状态),赋值部分保持原位置不动。

变量声明和函数声明提升的优先级

函数声明提升的优先级要高于变量声明提升。

先看一个简单示例:

console.log(say); //输出:[Function: say]

function say(){
  console.log('1');
};

var say = '2';

console.log(say); //输出'2'

解析:本例中声明的函数和变量同名都是say,且函数声明在先,变量声明在后,按理说第一次打印say值预期会是undefined,然而结果是[Function: say]。

预编译阶段进行变量声明提升和函数声明提升后,上述代码执行效果等同于:

var say = function (){ //函数声明(包括定义)提升
  console.log('1');
};

var say; //只是声明,并不会覆盖say的值

console.log(say); //故输出:[Function: say]

say = '2'; //此时say会被覆盖

console.log(say); //输出'2'

总结:同名情况下,函数声明提升优先级要高于变量声明提升,且提升后该函数声明定义不会被提升后的同名变量声明所覆盖,但是会被后续顺序执行的同名变量赋值所覆盖。


Adrain
205 声望16 粉丝

当你凝视深渊的时候 深渊也在凝视你。


引用和评论

0 条评论