图解 Google V8 # 07:作用域链:V8是如何查找变量的?

简介: 图解 Google V8 # 07:作用域链:V8是如何查找变量的?

说明

图解 Google V8 学习笔记



作用域和作用域链


作用域就是存放变量和函数的地方,作用域链就是将一个个作用域串起来,实现变量查找的路径。


  • 全局环境有全局作用域,全局作用域中存放了全局变量和全局函数。
  • 每个函数也有自己的作用域,函数作用域中存放了函数中定义的变量。



例子1

var name = 'kaimo'
var type = 'global'
function foo(){
    var name = 'foo'
    console.log(name)
    console.log(type)
}
function bar(){
    var name = 'bar'
    var type = 'function'
    foo()
}
bar()


输出结果如下:name 的值为 foo,这个应该没什么疑问,但是 type 的值为 global,可能大家会有疑问,为什么不是 function,输出 global 的原因是什么 ?

4f5e27806dad49ac83c786022d9a2baa.png


下面先了解一下作用域的工作原理。



什么是函数作用域和全局作用域?


函数作用域:每个函数在执行时都需要查找自己的作用域,称为函数作用域,在执行阶段,执行一个函数时,当该函数需要使用某个变量或者调用了某个函数时,便会优先在该函数作用域中查找相关内容。

例子2

var kaimo = 313
var test
function kaimo_scope() {
    var name = 'kkk'
    console.log(name)
    console.log(type)
    console.log(test)
    var type = 'mmm'
    test = 1
    debugger
    console.log(kaimo)
}
kaimo_scope()


我们在控制台输入上面代码,可以看到在 debugger 的时候,右侧的 Scope 项里有 Local,这个就是当前函数 kaimo_scope 的作用域。


11cfcad697514da8b6f8f2c00e86157f.png


发现 kaimo_scope 里面还有个隐藏变量 this:V8 会默认将隐藏变量 this 存放到作用域中。


而 kaimo_scope 里面用到的 test,kaimo,并没有在 Local 里,那么 V8 应该如何获取这些变量?


   如果在当前函数作用域中没有查找到变量,那么 V8 会去全局作用域中去查找,这个查找的线路就称为作用域链。


6dc7e4448b014d62817ac872bc0a5512.png


全局作用域是在 V8 启动过程中就创建了,且一直保存在内存中不会被销毁的,直至 V8 退出。 而函数作用域是在执行该函数时创建的,当函数执行结束之后,函数作用域就随之被销毁掉了。



作用域链是怎么工作的?


我们在来分析一下例子1:


4f5e27806dad49ac83c786022d9a2baa.png



首先 V8 启动时:全局作用域里有 this,window 等全局变量。

3e75fc9e9c2e44a8964f763a4d392648.png

V8 启动之后,先编译顶层代码:会将顶层定义的变量和声明的函数都添加到全局作用域中。

104691fd78d142fd94aee64c2ff3f96d.png

编译完成,V8 进入执行状态,我们可以将代码大致分开成下面两个部分

// 解析阶段-实现变量提升
var name = undefined
var type = undefined
function foo(){
    var name = 'foo'
    console.log(name)
    console.log(type)
}
function bar(){
    var name = 'bar'
    var type = 'function'
    foo()
}
// 执行阶段
name = 'kaimo'
type = 'global'
bar()



当 V8 执行 bar 函数的时候,也需要进行编译和执行,在编译阶段,V8 会为 bar 函数创建函数作用域:

3380fddfff7c40f98ec2a5ab584826e2.png


同样,当 V8 执行 foo 函数的时候,也需要进行编译和执行,在编译阶段,V8 会为 foo 函数创建函数作用域:

561e06463ee3423a911e9219774a7717.png



作用域已经找好了,那么 foo 函数查找变量的路径到底是什么?


  1. 沿着 foo 函数作用域 –> bar 函数作用域 –> 全局作用域?
  2. 沿着 foo 函数作用域 —> 全局作用域?


bar 和 foo 函数的外部代码都是全局代码,所以无论你是在 bar 函数中查找变量,还是在 foo 函数中查找变量,其查找顺序都是按照当前函数作用域 –> 全局作用域这个路径来的。


作用域链的路径就是按照词法作用域来实现的。


   静态作用域:词法作用域是根据函数在代码中的位置来确定的,作用域是在声明函数时就确定好的了,所以也将词法作用域称为静态作用域,JavaScript 是基于词法作用域的。


   动态作用域:并不关心函数和作用域是如何声明以及在何处声明的,只关心它们从何处调用。动态作用域是基于调用栈的,而不是基于函数定义的位置的。


我们把例子1改一下:


例子3:

var name = 'kaimo'
var type = 'global'
function bar(){
    var name = 'bar'
    var type = 'function'
    function foo(){
      var name = 'foo'
      console.log(name)
      console.log(type)
  }
    foo()
}
bar()


结果如下:

6ba37296de7643908956501871d85db8.png


我们可以打个 debugger 看看,会有一个 Closure。


4767240174d14c81ab1ba10883fbf4c2.png


目录
相关文章
|
Web App开发 缓存 JavaScript
图解 Google V8 # 13:字节码(一):V8为什么又重新引入字节码?
图解 Google V8 # 13:字节码(一):V8为什么又重新引入字节码?
236 0
图解 Google V8 # 13:字节码(一):V8为什么又重新引入字节码?
|
2月前
Google Earth Engine(GEE)——两种线性回归计算(单变量linefit和多变量linearRegression)
Google Earth Engine(GEE)——两种线性回归计算(单变量linefit和多变量linearRegression)
58 0
|
缓存 JavaScript 前端开发
图解 Google V8 # 22 :关于内存泄漏、内存膨胀、频繁垃圾回收的解决策略(完结篇)
图解 Google V8 # 22 :关于内存泄漏、内存膨胀、频繁垃圾回收的解决策略(完结篇)
307 0
图解 Google V8 # 22 :关于内存泄漏、内存膨胀、频繁垃圾回收的解决策略(完结篇)
|
Web App开发 JavaScript 前端开发
图解 Google V8 # 21 :垃圾回收(二):V8是如何优化垃圾回收器执行效率的?
图解 Google V8 # 21 :垃圾回收(二):V8是如何优化垃圾回收器执行效率的?
118 0
图解 Google V8 # 21 :垃圾回收(二):V8是如何优化垃圾回收器执行效率的?
|
算法 JavaScript Java
图解 Google V8 # 20 :垃圾回收(一):V8的两个垃圾回收器是如何工作的?
图解 Google V8 # 20 :垃圾回收(一):V8的两个垃圾回收器是如何工作的?
102 0
图解 Google V8 # 20 :垃圾回收(一):V8的两个垃圾回收器是如何工作的?
|
前端开发 JavaScript
图解 Google V8 # 19 :异步编程(二):V8 是如何实现 async/await 的?
图解 Google V8 # 19 :异步编程(二):V8 是如何实现 async/await 的?
132 0
图解 Google V8 # 19 :异步编程(二):V8 是如何实现 async/await 的?
|
消息中间件 前端开发 JavaScript
图解 Google V8 # 18 :异步编程(一):V8是如何实现微任务的?
图解 Google V8 # 18 :异步编程(一):V8是如何实现微任务的?
422 0
图解 Google V8 # 18 :异步编程(一):V8是如何实现微任务的?
|
消息中间件 程序员 Android开发
图解 Google V8 # 17:消息队列:V8是怎么实现回调函数的?
图解 Google V8 # 17:消息队列:V8是怎么实现回调函数的?
109 0
图解 Google V8 # 17:消息队列:V8是怎么实现回调函数的?
|
存储 缓存 索引
图解 Google V8 # 16:V8是怎么通过内联缓存来提升函数执行效率的?
图解 Google V8 # 16:V8是怎么通过内联缓存来提升函数执行效率的?
145 0
图解 Google V8 # 16:V8是怎么通过内联缓存来提升函数执行效率的?
|
JavaScript 前端开发 编译器
图解 Google V8 # 15:隐藏类:如何在内存中快速查找对象属性?
图解 Google V8 # 15:隐藏类:如何在内存中快速查找对象属性?
140 0
图解 Google V8 # 15:隐藏类:如何在内存中快速查找对象属性?