重学javaScript (六)| 函数的执行,作用域链

827 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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,明明它调用的位置,也在定义的函数的前边,为什么它就不会出现变量提升呢,我们接着往下看

全局函数的执行过程

我们先来一步一步分析一下上面的那个例子

  1. 当js引擎开始解析我们代码的时候,它会先做这么一个事情,先生成一个全局的对象globject,里边存放全局定义的一些变量,以及js自带的一些函数的变量。以及生成一个全局的函数执行栈,以及全局执行上下文

image.png 2. 这个全局对象也就是GO遇到基础数据类型和特殊数据类型(函数)的赋值方式是不同的

  • 基础数据类型: 赋值为undefined

  • 特殊数据类型(函数):当js引擎遇到函数时,会提前在内存中开辟一块空间来存储函数的父级作用域以及函数的执行体,以本例子为例,GO的a指向了函数的内存地址,0xxx00

image.png

  1. 这个时候函数解析完成,开始进入代码的执行阶段,全局执行上下文中,会生成一个VO指向GO,执行的时候会先从VO找,遇到console.log(a())时,会从VO中找到函数的内存地址0X011

  2. 这个时候函数会生成一个函数执行上下文并加入执行栈里,同时这个执行上下文里会同时生成一个vo对象vo其实指向AOAO就是类似于GO的东西,主要存的是函数体内部的定义的一些变量

image.png

  1. 当函数执行的时候遇到相对于的变量也是从VO中寻找对应的变量然后来执行,跟在全局中执行代码是一模一样的,执行完之后,这个函数上下文出栈,它生成的AO一般会被销毁,而最开始放置父级作用域和函数体的内存却是一直都存在的

image.png

  1. 如果当函数再次被调用的时候,我们的js引擎就会重复一遍1到5这个几个步骤

作用域链 scope chain

当我们查找一个变量时,真实的查找路径是按照作用域链来查找的

先来看下作用域链的定义

代码在环境中执行的时候,会创建变量对象的一个作用域链。这个作用域链保证了对执行环境中所有的变量和函数的有序访问。当所需要的变量在当前的作用域中没有找到时,它就会一层一层向上查找,直到最后在全局作用域中查找。

一个执行上下文的作用域链等于Ao+parent scope 也就是AO+加上父级作用域链 注:函数的父级作用域链是在只跟函数的编译阶段的位置有关!与执行位置无关

来看一个小栗子

var name = 'juejin'

foo()

function foo () {

  function bar () {
    console.log(name)
  }

  bar()
}

结果

image.png

  1. foo的作用域链是父级作用域链+VO,它的作用域是全局的作用域也就是GO,所以foo的作用域链是自己的V0+GO
  2. bar定义的位置是foo里边,所以它的父级作用域就是foo的作用域链,所以它的作用域链就是自己的V0+foo的VO+GO 3.按照作用域链一层一层查找,name的值打印为juejin