先简单说下执行栈是什么:
js执行代码时会创造一个执行上下文(里面包括执行代码所需要的信息),比如下面代码
function foo(){
let a=1
return function b(){
console.log(a)
}
b()
}
foo()
调用函数foo时先创建了函数foo的执行上下文,执行到return 的时候又在执行栈里添加了新的函数b的执行上下文,等函数b执行完了之后就把函数b的执行上下文从执行栈里踢出去,再然后foo的执行上下文也踢出去
再详细说一下执行上下文
在es5里它包含了下面三个内容
- lexical environment:词法环境,当获取变量时使用。
- variable environment:变量环境,当声明变量时使用。
- this value:this 值。
但在es6之后是这个样子的
- lexical environment:词法环境(环境记录+外部词法引用),当获取变量或者 this 值时使用。
- variable environment:变量环境,当声明变量时使用。
- code evaluation state:用于恢复代码执行位置。
- Function:执行的任务是函数时使用,表示正在被执行的函数。
- ScriptOrModule:执行的任务是脚本或者模块时使用,表示正在被执行的代码。
- Realm:使用的基础库和内置对象实例。
- Generator:仅生成器上下文有这个属性,表示当前生成器。
会生成块级作用域
所以:
for(var i=0;i<5;i++){
setTimeout(()=>{console.log(i)},1000) // i打印出来的都是5
}
for(let i=0;i<5;i++){
setTimeout(()=>{console.log(i)},1000) // i打印出来的是0,1,2,3,4
}
并且
var、let、const class和function 在执行上下文的创建阶段时,js会在创建阶段执行上下文就将声明的变量、函数和类都创建,
但是var 声明的变量会被初始化为undefined,而 let / const / class 声明的变量不会被初始化 状态为 uninitialized ,并且禁止访问,function 关键字的声明会直接将函数体赋值给对应的函数名。
而且除了var之外,后面几个关键字声明的标识符映射是存放在词法环境里,而var声明的标识符映射是存放在变量环境里
所以:
console.log(c);
let c =1;
//Uncaught ReferenceError: Cannot access 'c' before initialization
//但是var
console.log(d) //undefiend
var d=1;
再谈谈闭包
根据古典闭包定义( closure )包含:
- 环境部分(环境和标识符列表)
- 表达式部分
- 环境(函数的词法环境)
- 标识符列表( 函数中用到的未声明的变量 )
- 表达式(函数体)
其实闭包就是一个带有词法环境的函数
Comments | NOTHING