动画:一个底层运行函数的自白!
作者:互联网
大家好,我是一个函数,一个又累,又苦,又被广大开发者使用的反派角色。
自从世界各大语言诞生的时候,我也随之诞生。虽然经常出现在人们的眼前,但是我的出生环境和身世却很少被人所知。今天不得不借助鹿哥的平台,进行一次自白。
这么多年过去了,总是被别人当做“工具人”,却总是不能够被人理解,我真的很伤心。多亏遇到了鹿哥,给了我这一次自白的机会。
在各个语言中,虽然我的存在的形态不同,但是我还是我,一个不一样的烟火,只要被人敲一下代码,我的兄弟姐妹们就被创建出来。而且我们每个函数天生的命运已经被决定这一生要去做一件事,我们不得不听天由命。
1function fn(){
2// 要执行的任务
3......
4}
1function fn(a, b){
2 let result = null;
3 result = a + b;
4 return result;
5}
其实,我们也像人类一样,需要生存的环境,空气、食物和水,但是我们很少被人所周知。下面我就亲自介绍一下我们所生存环境——栈。
我生活的栈内存中,有属于自己的两间大房子,一间房子当做仓库,存放一些变量和变量对应的值。
另一间房子就是我的工作室,执行我天生体内就有的任务代码。我的每个兄弟姐妹都和我一样,都有独立的环境和两间大房子,只不过我们的命运不同。
每当我的上级 Boss(主线程)告诉我要开始工作了,我就开始在工作室从头到尾顺序执行体内所要处理的任务。
因为栈内存空间太小,只能存储一些变量的和值,我体内的任务代码占据空间太大,栈内存里放不下。所以当我出生的时候,我体内的代码会以一堆破字符串的形式存放在堆内存中(其他大仓库),我在栈内存中只存这个堆仓库的地址就行了,这样需要占据的栈空间就小很多了。
每当我的 Boss(主线程)告诉我,开始工作了,我就开始在我的存储变量的栈仓库和存储代码字符串的堆仓库中,取出所需要执行的值和字符串。先对取出的字符串转化为可执行代码,开始在一个全新私有栈内存环境下执行。将执行完的结果会返回给外界变量兄弟接收。
为了能让大家更好的记住我的运行机制,不得不麻烦一下鹿哥把这个函数从定义到执行完毕给梳理一下。
大家好,我是鹿哥。一个迷人帅气、代码风骚、文章通俗的硬核男人。以上是函数的自白,它讲完了,该换鹿哥 BB 几句了。
我们启动运行程序,程序代码自上而下顺序执行,当遇到下面代码时,会发生什么呢?
1function fn(a, b){
2 let result = null;
3 result = a + b;
4 return result;
5}
6var a = fn(10, 10)
7console.log(a)
首先,遇到定义的函数 fn 时,会开辟一个栈内存空间,上边函数自己说了,栈内存空间会分为两部分,一部分是存储变量和对应的值。另一部分会分配给主线程用来执行代码。
由于函数是引用类型,会开辟一个堆内存空间,函数体内的所有执行代码字符串全部存放在堆中,当函数执行的时候才能叫做代码,这个时候只是存储在堆内的一堆破字符串而已。
程序继续自上而下执行,会将 a 变量存储在栈内存中,但是它对应的值就是函数执行后的返回值。
1var a = fn(10, 10)
函数开始执行,每一次函数执行的目的就是把函数体内的代码(先从堆内存中取出那一堆破字符串,然后转化为代码)执行。而且是在一个全新私有的栈内存内执行。
函数体内的代码开始自上而下顺序执行,这个全新的私有作用域栈和之前我们说到的栈内存是一样的,也是由两部分组成。
首先将代码中的变量推入到栈中,然后根据代码的运算取出相对应的变量进行求值,最后 retrun,将计算的值返回给变量 a,此时 a 在栈内存中有了对应的值。
PS:如果函数体内有 return 则直接返回 return 的值,如果没有,则返回的是 undefined。
以上就是整个函数从定义到执行最后结束的底层过程,但是我们还没有更深入,我们上边说到了栈中执行的两部分专业名词叫做 VO 阶段和 AO 阶段,再深入就会涉及到 JS 的编译过程,又可以作为一大部分去讲解。
通过上边函数底层的运行机制,我们有很多疑问得到解惑,比如为什么函数内声明的变量,函数外部不可访问?
因为函数执行,又重新创建了一个全新私有作用域,外界是不能直接访问该私有作用域栈内存中的值的,只有通过 return 的方式把值返回出去或者通过某种方式暴露出去。
函数定义的时候,函数体不执行的原因?
定义函数时,让对应的变量指向该堆内存的地址。我们只不过是在堆内存存了一堆破字符串而已,而不是代码,无法执行。只有当调用函数的时候,从堆内存中取出字符串转化为可执行的代码,函数体才可以执行。
通过这篇函数底层的运行机制,我们之前存在的一些对函数的疑问都一目了然。虽然像这种函数的底层运行机制看起来不难,但是我们自学中很难去梳理把握全面。
一些大厂的面试题,虽然看起来表面感觉很复杂,如果通过鹿哥上述对代码的整个底层运作拆解,再复杂的题目对你来说也会游刃有余。
不信来个阿里的面试题,欢迎在留言区写出你的答案和分析的过程。
1let a = {
2 n:1
3}
4let b = a;
5
6a.x = a = {
7 n:2
8}
9console.log(a.x)
10console.log(b)
提示:对象和函数都是引用类型,都存放在堆中的。
最后
这么硬核的文章,加上骚气的鹿哥讲解,不要放弃手中点赞的权利,相信我,你是最“胖”的!
标签:自白,函数,动画,代码,内存,鹿哥,执行,变量,底层 来源: https://blog.51cto.com/15064450/2599762