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

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容