文章目录
JS函数名与变量名重名的问题
由于JavaScript的预编译会导致变量声明提升,声明式函数整体提升,因此当函数名与变量名重名的时候将会产生问题。
变量以及变量表达式(包括函数的表达式声明)在预编译阶段都只是声明提升,而没有赋值,值都为undefined。
而声明式函数在预编译阶段将会整体提升。
而在编译过程中,提升也是有顺序的:参数 > 函数 > 变量
因此函数与变量的预编译遵循以下规则:
- 函数声明置顶;
- 变量声明置顶;
- 遇到同名时,变量声明比函数声明更优先(置顶);
- 变量不会重复声明;
- 直接变量声明时赋值,在预编译解析时,会拆成声明和赋值2个部分,声明会置顶,而赋值将保留在原来的位置;
- 声明的提升只会提升到当前作用域下的顶层,而不会提升到作用域之外。
- 预编译过程中形成的作用域链的查找顺序是由里向外,JS执行顺序是自上往下的。
- 当函数被声明在判断语句内部时,JS会将函数声明当成表达式(赋值声明),即不会提升
//变量或变量表达式声明提升
var a = 10;
//这相当于
var a; //这就是发生在预编译阶段的变量的声明提升,在这个阶段变量只有声明,而没有赋值,为undefined
a = 10; //这是在执行阶段,将会对变量赋值
//函数表达式
let fun = function(){
console.log('hello simon');
}
//这相当于
let fun; //这就是发生在预编译阶段的变量的声明提升,在这个阶段变量只有声明,而没有赋值,为undefined
fun = function(){ //执行阶段才会进行定义
console.log('hello simon');
}
//声明式函数整体提升
var a = 10;
function fun(){
var b = 11;
}
//这相当于
function fun(){
var b;
b = 11;
}
var a;
a = 10;
题1
由于函数声明是整体提升的,因此下面代码中,a函数声明在前,而变量a赋值在后,因此a是一个变量,而不是一个函数了,a执行出错
var a = 10;
function a(){
console.log(a);
}
a();
//相当于
var a = function(){
console.log(a);
};
a = 10;
a(); //将报错,此时a是一个变量了
题2
预编译过程中形成的作用域链的查找顺序是由里向外,JS执行顺序是自上往下的。因此下面代码中a = 1时,if的作用域中并不存在a变量,所以会将1赋值给全局中的a变量。
var a = 0;
if(true){
a = 1; //作用域中不存在a变量,因此会通过作用域链向外查找,找到全局中的a变量,并将1赋值
function a(){} //这是函数声明,相当于定义了一个函数a,并给其分配了内存
a = 21; //此时作用域中已经存在了a变量(a函数),因此该赋值将直接赋值给作用域中的a而不是全局的a
console.log('里面', a); //21,由于作用域中存在a变量了,因此直接打印作用域中的a
}
console.log('外面', a); //1,全局作用域中存在a变量,并赋值为1,因此打印1
//相当于
var a;
a = 0;
if(true){
a = 1;
function a(){} //由于函数是定义在判断语句中,所以没有声明提升
a = 21;
console.log('里面', a) //21
}
console.log('外面', a); //1
题3
函数会整体提升,因此b函数会被提升到全局作用域中的顶部,而a函数被提升到b函数作用域顶部。注意函数是自上而下执行的
var a = 1;
function b(){
a = 10;
return;
function a(){
console.log(a);
}
}
b();
console.log(a);
//相当于
function b(){ //整体提升
function a(){ //整体提升
console.log(a);
}
a = 10;
return;
}
var a;
a = 1;
b(); //执行b函数后,内部函数a会声明,因此作用域内存在一个a变量(函数),a = 10会赋值给a函数,而不是全局中的a变量了
console.log(a); //1 打印的是全局的a变量,内部的a变量无法获取到