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'
总结:同名情况下,函数声明提升优先级要高于变量声明提升,且提升后该函数声明定义不会被提升后的同名变量声明所覆盖,但是会被后续顺序执行的同名变量赋值所覆盖。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。