美文网首页
详细解读函数的底层处理机制

详细解读函数的底层处理机制

作者: 小生菜呢 | 来源:发表于2021-07-28 21:11 被阅读0次

JS中的堆(Heap)栈(Stack)内存

都是在计算机内存中开辟的空间
  • 栈内存 Stack:ECStack(Execution [ˌeksɪˈkjuːʃn] Context Stack)
    1.存储原始值类型
    2.代码执行的环境
  • 堆内存 Heap:
    1.存储对象值类型
EC(Execution [ˌeksɪˈkjuːʃn] Context )执行上下文:区分代码执行的环境
  • 常见上下文分类:
  1. 全局上下文EC(G)
    2.函数私有上下文EC(?)
    3.块级私有上下文EC(Bolck)
  • 产生私有上下文->进栈执行->出栈释放(可能释放)
  • 变量对象:当前上下文中,用来存储声明的变量的地方
    1.VO(Varibale Object):VO(G) 或者 VO(BLOCK)
    2.AO(Active Object):AO(?)

GO(Global Object)全局对象

  • window指向GO对象
  • 全局上下文中,基于var/function声明的变量是直接存储到GO
    对象上的,而基于let/const生命的变量才是存放在VO(G)中的

let 变量 = 值 的操作步骤

  • 第一步:创建值
    1.原始值类型:直接存储在栈内存中,按值操作
    number
    string
    boolean
    null
    undefined
    Symbol
    bigInt
  1. 对象类型值:按照堆内存地址来操作
    @1、对象:开辟一个堆内存空间(16进制地址)、一次存储对此昂的键值对、把空间地址赋值给变量
    @2、函数:内存空间存储三部分信息
    作用域[[scope]]:当前所处上下文
    函数体中的代码字符串
    当作普通对象存储的静态属性和方法[name&length]
  • 第二步:声明变量 declare
  • 第三步:变量和值关联在一起(赋值)defined

以代码为例来详细解读函数的底层处理机制

1 、

var x = 12
let y = 12
z = 14
console.log(x)
console.log(window.x)
console.log(y)
console.log(window.y)
console.log(z)
console.log(window.z)

全局代码执行,形成EC(G)全局执行上下文

  • EC(G)全局执行上下文
    VO(G):全局变量对象(基于let/const声明的全局变量存储在这里)
    y:13
    window -- > GO全局对象(基于var/function声明的全局变量存在这里)
    x:12
    z:14 // window.z = 14 直接设置在GO中,相当于省略了‘.window’
  • 代码执行:
    @1、console.log(x) :结果是 12 ;
    首先看VO(G)中是否存在,如果不存在 再去GO中看看是否存在,如果都不存在则报错:x is not defined
    @2、console.log(window.x):结果是12;
    直接到GO中找这个属性,如果不存在,则是undefined(因为是访问当前对象的某个成员,成员不存在的话结果是undefined,所以不是报错)
    @3、console.log(y):结果是13;
    基于let声明的变量存在VO(G)中
    @4、console.log(window.y):结果是undefined;

@5、console.log(z):结果是14;直接去GO中找
@6、console.log(window.z) :结果是14;直接去GO中找
2、

let x = [12,23]
function fn(y) {
  y[0] = 100
  y = [100]
  y[1] = 200
  console.log(y)
}
fn(x)
console.log(x)
image.png
文字版:

再重复一边

  • 计算机会开辟两两个空间:栈内存、堆内存
    @1、栈内存:存储原始值(值类型)、提供代码执行的环境
    @2、堆内存:存储对象值类型

  • 上边已经提到:凡是变量=xxx 都是:
    @1、先创建值
    @2、定义变量
    @3、赋值

代码执行步骤:
1、浏览器开辟一块空间-->栈内存-->ECStack代码执行环境栈
2、浏览器开辟一块空间-->堆内存-->-GO全局对象:16进制地址定为 0x000
@1、存放内置属性:
setTimeout,
setInterval,
requestAnimationFrame
.......
3、最开始肯定是全局代码执行,形成EC(G):全局执行上下文-->进栈执行
@1、全局执行上下文中,基于let/const声明的变量要存放到全部变量对象VO(G)中
4、代码自上而下执行
5、[12,23]是个数组,所以开辟一个堆内存来存储,16进制地址暂定为:0x001
@1、存储的内容有:
0:12,
1: 23,
length:2
.....
6、定义变量x
基于let声明,存储到VO(G)中
7、x和0x001关脸 x --> 0x001
8.VO(G)中存储的变量:
@1、x -------> 0x001
9、function xxx 也是定义一个变量,基于function/let声明的而变量存在GO中
@1、函数也是一个对象,所以开一个堆内存,16进制地址为:0x002
@2、存储的内容:

  • 作用域[[scope]]:函数在哪个上下文中创建,那么它的作用域就是哪个上下文(为作用域链做铺垫)
  • 函数代码字符串
  • 当作普通对象来存放键值对值(静态私有属性方法)
    (a)、name:fn
    (b)、length:1(形参个数)
    所以函数如果不执行,一点意义都没有
    [[重复:函数的作用域是在它创建的时候声明的]]
    @3、把10进制地址0x002赋值给fn

10、此时GO中存储的属性:
setTimeout,
setInterval,
requestAnimationFrame,
fn:0x002
..........
11、函数执行并传值:fn(x) ----> fn(0x001)
@1、产生全新的私有执行上下文EC(FUN),然后进栈执行[私有上下文中有个私有变量对象AO(FUN),用来存储私有上下文中声明的变量]
@2、代码正式执行前:

  • 初始化作用域链<EC(FUN)当前自己的私有上下文,EC(G)函数的作用域(上级执行上下文)>,明确变量的归属
  • 初始化THIS指向:window
  • 初始化aguments:{0:’0x001‘}
    (aguments是类数组集合,存的是实参,不管有没有形参,只要传了实参,arguments中都会有值)
  • 形参赋值:y = 0x001
  • 变量提升:无
  • 形参和上下文中声明的变量都是私有的
  • 此时AO(G)中存的值是:
    y ---> 0x001

@3、代码执行

 y[0] = 100  
 y = [100]
 y[1] = 200
 console.log(y)
  • 第一行:是自己的私有变量,地址指向0x001,所以把堆内存0x001中索引为0的值改成100
    此时0x001中存储的内容变成:
    0:100,
    1:23,
    length:2,
    ......
    y虽然是私有变量,但是和全局变量x指向的同一个地址,全局x的值也受影响,此时x的值为:[100,23]

  • 第二行 y = [100] :[100]是个数组,所以再重新开一个堆内存0x003,用来存储这个数组
    0:100,
    length:1
    .....
    此时jy指向新的堆内存地址0x003

  • 第三行:根据y执行的新地址,在0x003这个堆内存中增加了一个索引为1的值,此时0x003的存储内容是:
    0:100,
    1:200,
    length:2
    .....

  • 第四行:输出y,y是私有的,地址指向0x003
    所以输出值为:*[100,200]

@4、fn执行完后,出栈释放

12、函数执行完,执行全局代码:console.log(x)
刚才已经提到 x指向0x001
0x001中的值在fn执行的时候已给修改成:[100,23]

全局代码在浏览器关掉的时候才会释放

以上就是整个代码的运行机制

总结:

1、创建函数的过程:

@1、开辟堆内存[16进制地址]
@2、存储的内容

  • 作用域[[scope]]:在哪个上下文中创建的,那么它的作用于就是哪个上下文(为作用域链做铺垫)
  • 函数代码字符串
  • 作为普通对象有的属性:
    name:函数名
    length:形参的个数

@3、把16进制空间地址赋值给变量(函数名)即可

2、普通函数执行要做的事情:

@1、产生全新的私有执行上下文EC(?),然后进栈执行
私有上下文中有私有变量对象AO(G),用来存放私有变量
@2、代码执行前还要做的事情:

  • 初始化作用域链:<EC(?)自己私有的执行上下文,EC(G)函数的作用域(上级执行上下文)>
  • 初始化this
  • 初始化arguments:类数组集合,存储实参
  • 形参赋值
  • 变量提升
    [[形参和在私有上下文中声明的变量都是私有变量,存在AO中]]
    @3、代码正式执行
    @4.执行完出栈(如果被外部占用,不出栈,类如闭包)
3、作用域链

<自己私有上下文,作用域(上级上下文)>
@1、在私有上下文中遇到个变量,首先看是否为私有变量(看AO中是否存在),如果是私有变量,则接下来操作的都是私有变量(和外界都没有关系)
@2、如果不是自己私有的,则去上级上下文中查找,如果是上级的,则操作的都是上级的
@3、如果也不是上级的,则继续找上级的上级的上下文....直到找到EC(G)为止
@4 如果EC(G)中也没有,则:

  • 如果是获取变量值,则报错
  • 如果是设置变量值,则相当于给window设置对应的属性
function fn() {
/* fn执行,形成私有上下文EC(FN)
*      AO(FN):
*      作用域链:<EC(FN),EC(G)>
*      形参赋值:--
*      变量提升:--
*      代码执行
*/
  console.log(n)  //  既不是形参,也没有在这个上下文中声明过,所以AO(FN)中没有,沿着作用域链往上找,全局GO也没有,所以会报错:n is not defined
}
fn()

如果是:

function fn() {
  n = 100   // 相当于给GO中设置一个属性n,值是100 --> window.n = 100
}
fn()
console.log(n, window.n) // 100  100

相关文章

  • 详细解读函数的底层处理机制

    JS中的堆(Heap)栈(Stack)内存 都是在计算机内存中开辟的空间 栈内存 Stack:ECStack(Ex...

  • 第七章、mixin详解

    导读 在第五章 views 解读时,我们已经详细解读了 http 方法对应的处理函数,但是却没发现这些处理函数在哪...

  • 函数的底层处理机制

    js上下文分类 js上下文(哪一个区域下执行)分类: 1.全局上下文EC(G) 2.函数执行形成...

  • Jquery中$.get(),$.post(),$.ajax()

    详细解读Jquery各Ajax函数: $.get(),$.post(),$.ajax(),$.getJSON() ...

  • 关于SCN

    参考链接 深入剖析 - Oracle SCN机制详细解读 详解Oracle scn Oracle SCN详解 or...

  • 1. 消息机制的简单使用

    前言:Objective-C 的底层实现是运行时机制, OC的函数是动态调用过程. 其中最主要的是消息机制. 函数...

  • 对于文章图片中水印的问题

    处理图片水印:下面的函数可以处理局部的水印,详细请看函数文档该函数适合处理位置固定的水印. 在水印位置覆盖新的水印...

  • Android面试相关—AsyncTask机制

    Asynctask和handler一样,也是Android中异步消息的处理机制,AsyncTask机制底层封装了线...

  • PHP递归函数

    很多同学在学习递归函数的时候会感到头晕,无法搞清楚递归函数的原理和运行机制,本文将给大家详细讲解递归函数的运行机制...

  • js的事件处理机制

    事件处理函数的机制: 在给某个函数添加了事件处理函数后,一旦事件发生,相应的javascript代码就会得到执行。...

网友评论

      本文标题:详细解读函数的底层处理机制

      本文链接:https://www.haomeiwen.com/subject/gusomltx.html