【】js代码在执行之前会先进行编译
,经过编译后会生成“执行上下文
”和“可执行代码
”。执行上下文是js执行一段代码时的运行环境,执行上下文中存在一个“变量环境的对象
”,该对象中保存变量提升的内容。
【】js代码是一段一段的执行,有三种代码才算是一段代码:1、执行全局代码时会编译全局代码并创建全局执行上下文;2、调用一个函数时函数内部代码会被编译并创建函数执行上下文; 3、eval函数代码也会被编译并创建执行上下文。其中全局上下文只有一份,函数执行上下文在函数执行结束后被销毁
【】执行上下文栈
(调用栈)就是用来管理函数调用关系的一种数据结构,是js引擎追踪函数执行的一个机制。开发中可以通过打断点来查看调用栈,也可以使用 console.trace()
来输出当前的函数调用关系。注意,调用栈是有大小的,当入栈的执行上下文超过一定数目,js引擎就会报错,提示栈溢出
var a = 2
function add(b,c){
return b+c
}
function addAll(b,c){
var d = 10
result = add(b,c)
return a+result+d
}
addAll(3,6)
【】块级作用域
是通过词法环境
的栈结构来实现的,而变量提升
是通过变量环境
来实现,通过这两者的结合,JavaScript 引擎也就同时支持了变量提升和块级作用域了。在词法环境内部,维护了一个小型栈结构,栈底是函数最外层的变量,进入一个作用域块后,就会把该作用域块内部的变量压到栈顶;当作用域执行完成之后,该作用域的信息就会从栈顶弹出,这就是词法环境的结构
function foo(){
var a = 1
let b = 2
{
let b = 3
var c = 4
let d = 5
console.log(a)
console.log(b)
}
console.log(b)
console.log(c)
console.log(d)
}
foo()
【】在每个执行上下文的变量环境中,都包含了一个外部引用
,用来指向外部的执行上下文,这个外部引用称为outer
。当一个代码使用了变量,js引擎首先会在当前执行上下文查找该变量,若没查找到,那么js引擎会继续在outer所指向的执行上下文中查找,这个查找的链条就称为作用域链
。而外部的引用指向是由代码中函数声明的位置
来决定的,因为js在执行过程中作用域链是由词法作用域(静态作用域)
决定的,即词法作用域是代码编译阶段就决定好的,和函数是怎么调用的没有关系
【】关于暂时性死区
,在ES6中有这样一个特性:如果区块中存在 let 和 const 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。因为JS清楚地感知到了 name 是用 let 声明在当前这个代码块内的,所以会给这个变量name加上暂时性死区的限制,然后它就不需要向外面作用域查找了。
var name='jack';
{
name='bob';
let name; //Uncaught ReferenceError: Cannot access 'name' before initialization
}
{
name='bob'; //此时name值被成功修改为了bob
}
【】在js中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中,我们就把这些变量的集合称为闭包
。使用闭包的情况有很多,比如自执行函数、节流防抖、使用回调函数