变量提升
变量提升:在当前上下文中(全局/私有/块级),js代码自上而下执行之前,浏览器会提前处理一些事情(可以理解为词法解析的一个环节,词法解析一定要发生在代码执行之前):会把当前上下文中所有带var/function关键字的进行提前声明或定义。
- 声明declare:如 var a;
- 定义defined:如 a = 10;
- 带var的只会提前声明,不定义
- 带function的会提前声明并定义
- 代码执行前全局上下文中带var的变量提升
console.log(a);
var a = 12;
a = 13;
console.log(a);
上面代码中执行步骤为:
- 变量提升 - 声明变量a var a; 默认值为undefined
- 执行console.log(a);输出undefined
- 创建一个值12,然后再让变量a与12关联; a = 12;
- 创建一个值13,然后再让变量a与13关联; a = 13;
- 最后执行console.log(a);输出13
- 代码执行前全局上下文中带function的变量提升
在调用函数func之前,该函数已经被声明并定义
func();
function func(){
console.log('变量提升');
}
- 全局上下文 ==判断体(if)中== 的变量提升
不管条件是否成立,都要进行变量提升 需要注意的是:条件中带function的在新版本浏览器中只会声明不会定义,而在老版本浏览器中是即声明又定义
- 老版本,如IE10以下:
- var a 全局上下文中声明一个a也相当于给window加一个a属性
- fn = 函数(声明+定义)
- 新版本
- var a 全局上下文中声明一个a也相当于给window加一个a属性
- fn;只声明不定义
console.log(a);//undefined
if(!('a' in window)){
var a = 1;
function func(){}
}
console.log(a);//undefined
//'a' in window 检测a是否为window的属性,因为是全局声明所以a是window的属性,再取反后则条件不成立,则if里的代码不执行,所以两次都输出undefined
- 关于变量提升的练习题
fn();
function fn(){console.log(1)}
fn();
function fn(){console.log(2)}
fn();
var fn = function {console.log(3)}
fn();
function fn(){console.log(4)}
fn();
function fn(){console.log(5)}
fn();
上面代码中最终输出结果为 5 5 5 3 3 3
- 步骤分析:
- 首先进行带var和function的变量提升
- fn => 1;
- fn => 2;
- var fn;此处不再处理,因为上面处理function提升时已经有变量fn了,所以这里不再处理
- fn => 4;
- fn => 5;
- 变量提升以后fn对应的值是输出5
- 然后代码开始执行
- 第一次调用fn(); 输出的是5
- 第二次调用fn(); 输出的是5
- 第三次调用fn(); 输出的是5
- 执行到var fn...时对fn进行重新赋值,因为刚才在变量提升阶段并没有对这句代码进行处理,所以fn变为输出3
- 最后几次调用都是输出3
var foo = 1;
function bar(){
if(!foo){
var foo = 10;
}
console.log(foo);
}
bar();
步骤分析:
- 首先进行全局变量提升
- var foo
- function bar(){ ...}
- 全局变量提升后,开始代码执行,调用bar函数
- bar函数执行会形成一个私有的执行上下文
- 在函数私有上下文中,初始化步骤
- 初始化作用域链
- 初始化this
- 初始化arguments
- 形参赋值
- 变量提升: var foo; 不管判断题条件是否成立都先变量提升
- 然后函数中的代码执行,发现foo是函数的私有变量,并且只声明没赋值,所以取反后条件成立
- 给变量foo赋值为10
- 所以最后输出结果10