变量提升—JS代码执行的预处理机制
在“当前上下文”中,代码执行之前,浏览器首先会把所有带var/function关键字的进行提前声明或者定义。而其它如let/const/import/class声明的变量不存在变量提升
- var:声明提前
- function:声明提前,定义(赋值)提前
在全局上下文中,var声明的变量放在GO中,let/const 声明的变量放在VO中
- 推荐使用函数表达式,确保函数执行只能放在“创建函数”的下面,保证逻辑严谨性
const fn = function fn(){
console.log(1)
}
fn();
- 条件判断:在当前上下文中,变量提升阶段,不论条件是否成立,都要进行变量提升
-
- var:还是只声明
- function:判断体中的函数,在变量提升阶段,只声明不赋值「函数是个渣男」
console.log(a);//undefined
if (!('a' in window)) {
var a = 13;
}
console.log(a);//undefined
//var a,先声明,全局下,所以window.a = undefined
//!('a' in window)是false,所以a不会赋值,所以两个打印都是undefined
示例
console.log(a);//undefined
var a = 12;
console.log(b);//报错Uncaught ReferenceError: b is not defined
let b = 12;
console.log(fn);//function fn(){ console.log(2); }
function fn(){ console.log(1); }
console.log(fn);//function fn(){ console.log(2); }
var fn = 12;
console.log(fn);//12
function fn(){ console.log(2); }
console.log(fn);//12
var a = 1
function test(){
console.log(a)//1
a=0////执行时先找局部作用域,找不到a,就去上级作用域找,找到了a,修改。所以此处修改的是上级作用域(全局作用域)中的a
console.log(a)//0
}
test()
console.log(a)//0
============================
var a = 1
function test(a){
console.log(a)//1
a=0//执行时先找局部作用域,找到了a,就修改。所以此处修改的是局部作用域中的a,上级作用域(全局作用域)中的a不会被修改
console.log(a)//0
}
test(a)
console.log(a)//1
块级上下文和函数
除“函数和对象”的大括号外「例如:判断体/循环体/代码块…」,如果在大括号中出现了 let/const/function/class 等关键词声明变量,则当前大括号会产生一个“块级私有上下文”;
它的上级上下文是所处的环境;
var不产生块级上下文,也不受块级上下文的影响;
//忽略报错的影响
console.log(a);
console.log(b);
var a = 12;
let b = 13;
if (1 == 1) {
//产生一个块级上下文【前提是在支持ES6语法规范的浏览器中,才会产生块级上下文(ES6语法才出现)】
console.log(a);
console.log(b);
var a = 100;
let b = 200;
console.log(a);
console.log(b);
}
console.log(a);
console.log(b);
函数是个渣男
若存在判断条件的函数,
旧版浏览器:函数在全局上下文中会声明和定义
新版浏览器:为了向后兼容ES5,向前兼容ES6,对函数处理的机制做了一些改变。
@1 变量提升阶段,如果function出现在“除函数和对象”的大括号之内,此时不再是声明+定义函数,而是只声明,不赋值了。
@2 因为“function(){}”既被全局声明过,也被块级声明+定义过,所以执行到这一行代码的时候,会把块上下文中,当前这行代码 以前对foo所有的操作 ,同步给全局。但是之后再对foo的操作,只和私有块级上下文有关系,和全局没关系了。【函数是渣男,一等公民】
console.log(foo);
if (1 === 1) {
console.log(foo);
function foo() {}
foo = 1;
console.log(foo);
}
console.log(foo);
- 循环中的块级上下文
/* EC(G)
VO(G) / GO
f -> 0x000 [[scope]]:EC(G) “return true”
g -> 0x001 [[scope]]:EC(G) “return false”
变量提升:- -
*/
f = function () {return true;};
g = function () {return false;};
(function () {
/!*
EC(AN)
AO(AN)
g
作用域链:<EC(AN),EC(G)>
初始THIS:window / undefined
初始ARG:...
形参赋值:--
变量提升:function g; 只声明不定义了「因为其出现在判断体中」
*!/
if (g() && [] == ![]) { //Uncaught TypeError: g is not a function
f = function () {return false;}
function g() {return true;}
}
})();
console.log(f());
console.log(g());