Visual LabINTERACTIVE LEARNING
JavaScript 内核精校教程

JavaScript 执行上下文与闭包

沿“创建上下文 → 绑定环境 → 压入调用栈 → 闭包保留引用”理解代码如何真正执行。

执行上下文闭包作用域
进阶预计 18 分钟查看源文 ↗

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"

飞书画板

全局代码执行过程
  1. 代码初始化时,会创建一个GO
  2. 代码解析过程,会把全局声明的变量,放到GO中,作为它的一个属性,此时值为undefined,如果遇到全局的函数声明,会立即创建函数对象====函数可以先调用,后声明 这里就是变量作用域提升
  3. 代码执行时,执行栈的VO就是GO,赋值语句会把值赋予给Go中声明的属性
  4. 如果遇到函数调用,则会立即创建一个新的执行栈
函数执行过程
  1. 函数执行时,会首先创建一个AO,作为执行栈的VOAO是以函数的arguments作为初始值创建
  2. 如果函数中有变量声明,则会加到AO中作为一个属性,函数声明也会被立即创建函数 ====(注意VO并不等同于函数本身这里就是变量作用域提升
  3. 函数声明的时候作用域链就已经确定,和调用的位置无关,亦即创建函数的时候已经确定了scope
官方描述
  1. 每一个执行上下文会关联一个VO(Variable Object,变量对象),变量和函数声明会被添加到这个VO对象中

    • 当进入一个函数执行上下文时,会创建一个AO对象(Activation Object);
    • 这个AO对象会使用arguments作为初始化,并且初始值是传入的参数;
    • 如果函数有默认参数,则会在初始化后检测每个参数是否已被赋值,如无(为undefined),则使用默认参数赋值。 亦即给默认参数 传入的值是undefined的话,也是使用默认参数的值
    • 这个AO对象会作为执行上下文的VO来存放变量的初始化;
  2. 进入到一个执行上下文时,执行上下文也会关联一个作用域链(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算法 标记-清除算法

可达性 :从根出发,可达到的对象保留,不可达的对象则清除,解决了引用计数中循环引用的问题