你该知道的JS知识---声明提升

102 阅读4分钟

你该知道的JS知识---声明提升

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

  • 变量定义
    • 使用varlet关键字定义的变量,在没有赋值的情况下,该变量的值是undefined
    • 使用const关键字定义变量却不赋值,将会抛出错误
  • 变量作用域
    • 全局变量的作用域遍布全局
    • 局部变量的作用域仅在于函数内部及其嵌套函数的作用域
    • 函数内部的同名变量或参数优先级高于全局同名变量 在JavaScript中,如果变量或函数没有声明就被调用,会导致错误。
console.log(a);
// Uncaught ReferenceError: a is not defined

声明提升包括变量声明提升函数声明提升

  • 变量声明提升:通过varletconst声明的变量在代码执行之前被JavaScript引擎提升到当前作用域的顶部
  • 函数声明提升:通过函数声明的方式(非函数表达式)声明的函数在代码执行之前被JavaScript引擎提升到当前作用域的顶部,而且函数声明提升优先于变量声明提示
    JavaScript的代码在生成前。会先对代码进行编译,编译的一部分工作就是找到所有的声明,然后建立作用域将其关联起来,因此,在当前作用域内包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理。
    注意这里是声明会被提前处理,赋值并不会提前处理,定义声明是在编译阶段进行的,而赋值是在执行阶段进行的。也就是说声明提升了,赋值还会停留在原地等待执行。

变量声明提升

标准的变量声明提升:

console.log(a); //undefined
var a=2;
console.log(a); //2

等价于

var a;
//变量声明提升至最顶部,默认赋值为undefined
cnosole.log(a);
//此时a变量已经声明了,但是还没有赋值,其默认值为undefined,因此此处打印“undefined”
a=2;
//把a赋值为2
console.log(a);
//a变量已经被赋值为2,因此此处打印结果为“2”

函数声明提升

函数的两种创建方式:

  • 函数声明
foo();   //输出‘bar’
function foo(){
    console.log('bar');
}
  • 函数表达式
foo();  //报错:foo is not a function
var foo=function(){
    console.log('bar');
};

为什么这两种代码输出的结果不一样呢?
原因在于,通过函数声明的方式,该函数声明(包括定义)会被提升至作用域的顶部,而表达式创建的方式则只提升了变量foo至作用域的顶部,此时的foo其值为undefined,调用foo自然会报错是,foo不是一个方法。
再来一个栗子看看

var foo=function(){
    console.log('1');
};
function foo(){
    console.log('2');
}
foo();  //'1'

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

var foo;
//变量声明提升
function foo(){
    console.log('2');
}
//函数声明提升
foo=function(){
    console.log('1');
};
//变量赋值,foo函数被覆盖
foo();  //'1'

总结

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

函数覆盖

函数的声明和变量都会被提升,但是函数声明会覆盖变量声明

var a;
function a(){};
console.log(a);
// 'function a(){}'

变量的重复声明是无用的,函数的重复声明会覆盖前面的声明(无论是变量还是函数声明) 重复声明无效

var a=1;
var a;
console.log(a);  //1

等效于:

var a;
//此时a的默认值为undefined
a=1;
console.log(a); //1

函数声明优先 由于函数声明提升优先于变量声明提升,所有变量的声明无效

var a;
function a(){
    console.log(1)
}
a(); //1

函数声明覆盖 后面的函数声明会覆盖前面的函数声明

a();  //2
function a(){
    console.log(1);
}
function a(){
    console.log(2);
}

所有,应该避免在同一作用域内重复声明