JavaScript 内核精校教程
JavaScript 执行上下文与闭包
沿“创建上下文 → 绑定环境 → 压入调用栈 → 闭包保留引用”理解代码如何真正执行。
JavaScript 的执行原理(ES3)
基础概念
堆内存(stack),JS对于复杂数据类型内存的分配会在堆内存中 开辟一块空间,并且将这块空间的指针返回值 给变量引用;
栈内存(Heap),JS对于原始数据类型内存的分配会在执行时, 直接在栈空间进行分配;
- GO(global object)
全局对象 - VO(variable object)
变量对象 - AO(activation object)
活动对象 - ECS(excution Contexts)
执行上下文
多个活跃的执行上下文会形成一个逻辑上的栈结构,成为执行栈,顶部是正在执行的执行上下文
执行过程
初始化GO
js引擎在执任何代码之前, 会初始化一个全局对象 GO
- 该对象所有的作用域都可以访问
- 包含一些默认的内置对象,Date、Array等
- 会根据当前环境注入一些全局对象,如浏览器是window
执行上下文
var message = "hello world"
function foo(num){
console.log(message) //undeifined
console.log(num) //123
var message ="hello you"
var num =23456
num= 90
}
foo(123)
var name= "anron"

全局代码执行过程
- 代码初始化时,会创建一个GO
- 代码解析过程,会把全局声明的变量,放到GO中,作为它的一个属性,此时值为undefined,如果遇到全局的函数声明,会立即创建函数对象====函数可以先调用,后声明 这里就是变量作用域提升
- 代码执行时,执行栈的
VO就是GO,赋值语句会把值赋予给Go中声明的属性 - 如果遇到函数调用,则会立即创建一个新的执行栈
函数执行过程
- 函数执行时,会首先创建一个
AO,作为执行栈的VO,AO是以函数的arguments作为初始值创建 - 如果函数中有变量声明,则会加到AO中作为一个属性,函数声明也会被立即创建函数 ====(注意VO并不等同于函数本身) 这里就是变量作用域提升
- 函数声明的时候作用域链就已经确定,和调用的位置无关,亦即创建函数的时候已经确定了scope
官方描述
每一个执行上下文会关联一个VO(Variable Object,变量对象),变量和函数声明会被添加到这个VO对象中
- 当进入一个函数执行上下文时,会创建一个AO对象(Activation Object);
- 这个AO对象会使用arguments作为初始化,并且初始值是传入的参数;
- 如果函数有默认参数,则会在初始化后检测每个参数是否已被赋值,如无(为undefined),则使用默认参数赋值。 亦即给默认参数 传入的值是undefined的话,也是使用默认参数的值
- 这个AO对象会作为执行上下文的VO来存放变量的初始化;
进入到一个执行上下文时,执行上下文也会关联一个作用域链(Scope Chain)
- 作用域链是一个对象列表,用于变量标识符的求值;
- 当进入一个执行上下文时,这个作用域链被创建,并且根据代码类型,添加一系列的对象;
浏览器 会对作用域链优化
面试题
// 面试题1
var n = 100
function foo(){
console.log(n) // undefined 创建AO时,函数还没有执行,所以内部的n已经在AO里了
return
var n = 10
}
//面试题2
foo(){
var a=b=100 // 这里只声明了a,而b会被当成一个全局变量
}
foo()
console.log(a) // error
console.log(b) // 100
b = 10 //在外层作用域,不声明变量,直接赋值,会被挂在全局作用域上
ES6
❗ 提示
一些名称会有改变,使用const let关键词,消除了作用域提升
词法环境: 是一种规范类型,用于在词法嵌套结构中定义关联的变量、函数等标识符
- 一个词法环境由环境记录 (Environment Record)和外部词法环境(Outer Lexical Environment)组成
- 一个词法环境经常用于关联一个函数声明、代码块语句、try...catch语句,当它们的代码被执行时,词法环境被创建出来

全局的Environment Record是一个合成的对象,包含

Declarative Environment
Global Environment
闭包原理
MDN解释:
一个函数和对其周围状态(词法环境 lexical environment)的引用捆绑在一起,这样的组合成为闭包
概念:
- 一个普通的函数function,如果它可以访问外层作用域的自由变量,那么这个函数和周围环境就是一个闭包;
- 从广义的角度来说:JavaScript中的函数都是闭包;
- 从狭义的角度来说:JavaScript中一个函数,如果访问了外层作用域的变量,那么它是一个闭包;
function foo(count){
function bar(num){
return count + num
}
return bar
}
var foo3= foo(3)
foo3(5) //8
闭包的内存图

内存管理
GC算法 标记-清除算法
可达性 :从根出发,可达到的对象保留,不可达的对象则清除,解决了引用计数中循环引用的问题