持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情
前言
在前边juejin.cn/post/710504… 这篇文章里只是简单的介绍了,简单的代码是怎么在js引擎里执行的,今天来看下函数的调用和执行都发生了什么
简单示例
先来看一段代码
console.log(a()) // 会打印个777
var name = 999
function a (data) {
var n = 10
var m = 100
console.log(777)
}
很显而易见,这段代码会打印个777,并不会像前边面对变量的时候,直接打印出来undefined,明明它调用的位置,也在定义的函数的前边,为什么它就不会出现变量提升呢,我们接着往下看
全局函数的执行过程
我们先来一步一步分析一下上面的那个例子
- 当js引擎开始解析我们代码的时候,它会先做这么一个事情,先生成一个全局的对象globject,里边存放全局定义的一些变量,以及js自带的一些函数的变量。以及生成一个全局的函数执行栈,以及全局执行上下文
2. 这个全局对象也就是
GO遇到基础数据类型和特殊数据类型(函数)的赋值方式是不同的
-
基础数据类型: 赋值为undefined
-
特殊数据类型(函数):当js引擎遇到函数时,会提前在内存中开辟一块空间来存储函数的
父级作用域以及函数的执行体,以本例子为例,GO的a指向了函数的内存地址,0xxx00
-
这个时候函数解析完成,开始进入代码的执行阶段,全局执行上下文中,会生成一个VO指向GO,执行的时候会先从VO找,遇到console.log(a())时,会从VO中找到函数的内存地址0X011
-
这个时候函数会生成一个
函数执行上下文并加入执行栈里,同时这个执行上下文里会同时生成一个vo对象,vo其实指向AO,AO就是类似于GO的东西,主要存的是函数体内部的定义的一些变量
- 当函数执行的时候遇到相对于的变量也是从VO中寻找对应的变量然后来执行,跟在全局中执行代码是一模一样的,执行完之后,这个函数上下文出栈,它生成的AO一般会被销毁,而最开始放置父级作用域和函数体的内存却是一直都存在的
- 如果当函数再次被调用的时候,我们的js引擎就会重复一遍1到5这个几个步骤
作用域链 scope chain
当我们查找一个变量时,真实的查找路径是按照作用域链来查找的
先来看下作用域链的定义
代码在环境中执行的时候,会创建变量对象的一个作用域链。这个作用域链保证了对执行环境中所有的变量和函数的有序访问。当所需要的变量在当前的作用域中没有找到时,它就会一层一层向上查找,直到最后在全局作用域中查找。
一个执行上下文的作用域链等于Ao+parent scope 也就是AO+加上父级作用域链
注:函数的父级作用域链是在只跟函数的编译阶段的位置有关!与执行位置无关
来看一个小栗子
var name = 'juejin'
foo()
function foo () {
function bar () {
console.log(name)
}
bar()
}
结果
- foo的作用域链是父级作用域链+VO,它的作用域是全局的作用域也就是GO,所以foo的作用域链是自己的V0+GO
- bar定义的位置是foo里边,所以它的父级作用域就是foo的作用域链,所以它的作用域链就是自己的V0+foo的VO+GO 3.按照作用域链一层一层查找,name的值打印为juejin