变量提升: 在当前上下文中(全局/私有/块级),JS代码自上而下执行之前,浏览器会提前处理一些事情(可以理解为词法解析的一个环节,词法解析一定发上在代码执行之前)
- 会把当前上下文中所有带VAR/FUNCTION关键字进行提前的声明或定义
- 带VAR的只会提前声明,带FUNCTION会提前的声明定义
- 在全局上下文中,我们声明过的变量VAR或者函数FUNCTION,也会给GO即window增加一个对应的属性,产生这样的一个映射机制,一个改另外一个也会跟着改
- 特殊情况,带条件判断的,不管条件是否成立,都要进行变量提升。新版本要向后兼容ES6,新版ES6函数有个块级作用域,对于FUNCTION只会提前声明,不会提前定义。老版本IE10及以下,对于FUNCTION不仅提前声明,还要提前赋值。
var a = 10;
-> 声明declare: var a;
-> 定义defined: a = 10;
/** 代码执行之前:全局上下文中的变量提升
* var a ; 默认值是undefined
**/
console.log(a); // undefined
var a = 12; // -> 创建值12,不需要声明a(变量提升阶段完成,完成的事情不会重新处理) a=12赋值
a = 13; // 全局变量 a=13
console.log(a); // 13
/**
* 全局上下文中的变量提升
* func = 函数 函数在这个阶段定义func和赋值都做了
**/
func(); // 函数可以执行
function func() {
// 全局上下文中的变量提升和这个a有关系么?没有关系。变量提升只会发生在当前上下文中
// 这个a 是函数里的,而且函数未执行前只是一堆字符串
var a= 12;
console.log('ok')
}
/**
* 实际开发中建议函数表达式创建函数,因为这样在变量提升阶段只会声明FUNC,不会赋值
**/
// func(); //如果在函数表达式前调用func(),就会报错 Uncaught TypeError: func is not function
var func = function() {
console.log('ok')
func() ;
var func = function AAA() {
//把原本作为值的函数表达式匿名函数”具名化“
//虽说是起了名字,但是这个名字不能在外面访问 =》也就是不会再当前上下文创建这个名字
//当函数执行,在形成的私有上下文中,会把这个具名化的名字作为私有上下文中的变量(值就是这个函数)来处理
console.log('OK')
AAA(); // 递归处理 而不用严格模式下不支持的arguments.callee
func(); // 递归调用这个也可以
} ;
// AAA(); // Uncaught ReferenceError:AAA is not defined
func();
setTimeout(function(){},1000);
setTimeout(function func(){
func();
},1000);
/**
* EC(G)变量提升
**/
console.log(a);// Uncaught ReferenceError:a is not defined
a=13;
console.log(a);
/**
* EC(G)变量提升
* 只有VAR/FUNCTION会变量提升(ES6中的LET和CONST不会)
**/
console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization 不能LET声明之前使用变量
let a = 12;
a = 13;
console.log(a);
/**
* 基于VAR或FUNCTION在"全局上下文"中声明的变量(全局变量)会映射GO(全局对象window)上一份,作为window的属性;
* 而且一个修改,另外一个也会跟着修改
**/
var a = 12;
console.log(a); // ->12 全局变量
console.log(window.a);; // -> 12 映射到GO上的属性a
window.a = 13;
console.log(a); // -> 13 映射机制一个修改另外一个也会被修改
/**
* EC(G) 全局上下文的变量提升
* 无论条件是否成立,都要进行变量提升(条件中带FUNCTION在新版本浏览器中只会提前声明,不会提前赋值)
* 【老版本】
* var a;
* func = 函数;
* 【新版本】
* var a; 全局上下文中声明a 相当于window.a
* func; 全局上下文中声明func,相当于window.func
**/
// 新版浏览器里面答案是 undefined undefined
// 老版本答案是:undefined func(){}
console.log(a, func);
if(!("a" in window)) {
// "a" in window 检测a是否为window的一个属性 true
// !("a" in window) false
var a = 1;
}
console.log(a) // undefined
/**
* EC(G) 变量提升
* 1) function fn() { console.log(1); } =》 声明加赋值 fn -> 1
* 2) function fn() { console.log(2); } =》 前面声明过了,只需要赋值 2
* 3)var fn = function () { console.log(3); } => var fn; 已经什么过了,不处理
* 4)function fn() { console.log(4); } =》 前面声明过了,只需要赋值 4
* 5)function fn() { console.log(5); } =》 前面声明过了,只需要赋值 5
* 变量提升的结果是:全局上下文中有一个全局变量fn,值是输出5的函数(此时window.fn -> 5)
**/
fn(); // 5
function fn() { console.log(1); } // 不再处理,变量提升处理过
fn(); // 5
function fn() { console.log(2); } // 不再处理,变量提升处理过
fn(); // 5
var fn = function () { console.log(3); } // var fn;声明不用管,但是赋值在变量提升阶段没处理过,此处需要处理 fn = window.fn -> 3
fn(); // 3
function fn() { console.log(4); } // 不再处理,变量提升处理过
fn(); // 3
function fn() { console.log(5); } // 不再处理,变量提升处理过
fn(); // 3
答案是: 5 5 5 3 3 3
var foo = 1;
function bar() {
if(!foo) {
var foo = 10;
}
console.log(foo);
}
bar(); // 10