Javascript 对象、函数、闭包——堆栈执行

97 阅读3分钟

对象堆栈执行

// 开辟了一个堆内存,内部的值为 { x: 100 }, 创建全局变量obj1, obj1指向这个内存地址
var obj1 = { x: 100 }
// 创建全局变量obj2,指向变量obj1指向的内存地址
var obj2 = obj1
// 通过赋值操作,改变了所指向的内存空间中的值
obj2.x = 200
// 此时 obj1、obj2仍然指向同一个内存地址,所以所访问的值是相同的 { x: 200 }

WechatIMG13.jpeg

// 开辟了一个堆内存,内部的值为 { x: 100 }, 创建全局变量obj1, obj1指向这个内存地址
var obj1 = { x: 100 }
// 创建全局变量obj2,指向变量obj1指向的内存地址
var obj2 = obj1
obj1.y = obj1 = { x: 200 }
  • 最后一行代码优先执行 obj1.y, 所以obj1obj2同时指向的内存空间多了一个值y = undefined
  • 随后,开辟一个新的内部值为{ x: 200 }的堆内存
  • 执行 obj1 = { x: 200 }, 变量obj1指向新的堆内存地址
  • 最后,执行 obj1.y = obj1
  • 此时 obj1 已经指向了新对象{ x: 200 }, 此时原堆内存仍然被obj2引用,所以未被Js垃圾回收机制回收,而obj1.y存在于obj1指向的原堆内存,所以原堆内存即obj2指向的内存地址中的值为{ x: 100, y: { x: 200 } }

WechatIMG14.png

函数堆栈执行

var arr = ['ka', 'rong', '帅']
function foo (obj) {
    // 函数体会以字符串形式存放在一个堆内存中,此堆内存地址与函数名相关联(foo: 0x000)
    obj[0] = 'hu'
    obj = ['ge']
    obj[1] = 'liudehua'
    console.log(obj)
}
foo(arr)
console.log(arr)
  • 创建函数和创建变量类似,函数名此时就可以看做一个变量名
  • 单独开辟一个堆内存用于存放函数体(字符串形式代码),当前内存地址会有一个16进制数值地址
  • 创建函数的时候,它的作用域[[scope]]就已经确定了(创建函数时所在的执行上下文)
  • 创建函数之后会将它的内存地址存放在栈区与对应的函数名进行关联
  • 函数执行时,即将函数对应的堆内存存放的代码(字符串形式)进行执行
  • 代码在执行的时候肯定需要有一个环境,此时就意味着函数在执行的时候会生成一个新的执行上下文(该函数的私有上下文)来管理函数体当中的代码。

函数执行步骤

  • 函数执行的时候会生成一个全新的私有上下文,它里面有一个AO用于管理这个上下文当中的变量
  • 确定作用域链 <当前执行上下文,上级作用域所在的执行上下文>
  • 确定this
  • 初始化arguments
  • 形参赋值: 它就相当于是变量声明,然后将声明的变量放置于AO
  • 变量提升
  • 代码执行

functionStack.jpg

闭包堆栈处理

var a = 1
function foo() {
    var b = 2
    return function(c) {
        console.log(c + b++)
    }
}
var f = foo()
f(5)
f(10)

closure.drawio.png

  • 闭包是一种机制
  • 当前上下文中的变量与其他上下文中的变量互不干扰
  • 当前上下文中的数据(堆内存)被当前上下文以外的上下文的变量所引用,这个数据就保存下来了
  • 函数调用的时候形成了一个全新的上下文,在函数调用之后当前上下文不被释放就是闭包
  • 闭包是一种机制,通过私有上下文来保护当中变量的机制
  • 我们可以认为当创建的某一个执行上下文不被释放的时候就形成了闭包
  • 保护、保存数据