图解 Google V8
李兵
前盛大创新院高级研究员
26763 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 25 讲
图解 Google V8
15
15
1.0x
00:00/00:00
登录|注册

06|作用域链:V8是如何查找变量的?

思考题
作用域链的工作原理
函数作用域和全局作用域
作用域的工作原理
作用域链的概念
作用域链

该思维导图由 AI 生成,仅供参考

你好,我是李兵。
在前面我们介绍了 JavaScript 的继承是基于原型链的,原型链将一个个原型对象串起来,从而实现对象属性的查找,今天我们要聊一个和原型链类似的话题,那就是作用域链。
作用域链就是将一个个作用域串起来,实现变量查找的路径。讨论作用域链,实际就是在讨论按照什么路径查找变量的问题。
我们知道,作用域就是存放变量和函数的地方,全局环境有全局作用域,全局作用域中存放了全局变量和全局函数。每个函数也有自己的作用域,函数作用域中存放了函数中定义的变量。
当在函数内部使用一个变量的时候,V8 便会去作用域中去查找。我们通过一段在函数内部查找变量的代码来具体看一下:
var name = '极客时间'
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 和 type,同时还定义了 bar 函数和 foo 函数,在 bar 函数中又再次定义了变量 name 和 type,在 foo 函数中再次定义了变量 name。
函数的调用关系是:在全局环境中调用 bar 函数,在 bar 函数中调用 foo 函数,在 foo 函数中打印出来变量 name 和 type 的值。
当执行到 foo 函数时,首先需要打印出变量 name 的值,而我们在三个地方都定义了变量 name,那么究竟应该使用哪个变量呢?
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

V8引擎如何查找变量?本文深入探讨了作用域链的概念和工作原理,阐述了作用域链在变量查找中的关键作用。通过详细的示例代码演示了在函数内部查找变量的过程,以及函数作用域和全局作用域的区别。文章还解释了V8引擎在执行代码时创建作用域的过程,并通过图示展示了作用域链的工作方式。此外,还介绍了词法作用域和动态作用域的区别。通过本文,读者可以快速了解V8引擎中作用域链的工作原理和JavaScript中作用域的相关概念。文章深入浅出地解释了V8引擎中变量查找的机制,为读者提供了清晰的技术视角和理解途径。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《图解 Google V8》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(31)

  • 最新
  • 精选
  • Geek_f
    老师,下面这题困扰很久了,不知道作用域链该怎么画,想请教下: var a = []; for(let i = 0;i<10;i++){ a[i]=function(){ console.log(i) } }; a[2]();

    作者回复: let定义的i会运行for的块级作用域中,每次执行一次循环,都会创建一个块级作用域。 在这个块级作用域中,你又定义了一个函数,而这个函数又引用了函数外部的i变量,那么这就产生了闭包,也就是说,所有块级作用域中的i都不会被销毁,你在这里执行了10次循环,那么也就创建了10个块级作用域,这十个块级作用域中的变量i都会被保存在内存中。 那么当你再次调用该a[n]()时,v8就会拿出闭包中的变量i,并将其打印出来,因为每个闭包中的i值都不同,所以a[n]()时,打印出来的值就是n,这个就非常符合直觉了。 但是如果你将for循环中的i变量声明改成var,那么并不会产生块级作用域,那么函数引用的i就是全局作用域中的了,由于全局作用域中只有一个,那么在执行for循环的时候,i的值会一直被改变,最后是10,所以最终你执行a[n]()时,无论n是多少,打印出来的都是10. 那么这就是bug之源了。

    2020-05-19
    3
    57
  • 刘大夫
    和 this 对比就很好记了,可以简单的理解为 this 是看函数的调用位置,作用域是看函数的声明位置。除了箭头函数等那些特殊的情况

    作者回复: 对,可以认为this是用来弥补JavaScript没有动态作用域特性的🤫

    2020-04-07
    3
    32
  • 杨越
    老师,昨天面试小红书,有个问题请教下: function f(){setTimeOut(fn,0)}面试官问我这种调用会不会导致内存溢出?

    作者回复: fn是啥?是函数f吧? 如果fn是f的话,那么不会溢出啊,因为这是异步调用,下次执行f函数时,已经在新的栈中执行了,所以当前栈不会发生溢出! 理解这个问题核心是理解事件循环和消息队列这套机制,这个专栏会有几篇文章介绍事件循环系统的,另外我的上个《浏览器专栏》对这块介绍的比较详细!

    2020-03-28
    6
    30
  • Bazinga
    老师,在大量数据时(百万级别) ,foreach循环比for循环的执行效率低,是因为什么

    作者回复: 因为foreach有函数回调过程啊,每次回调都要额外创建新的额外的栈贞,新的上下文,那么效率也就随之下来了

    2020-03-29
    4
    16
  • Jack.Huang
    根据ECMAScript最新规范,函数对象有一个[[Environment]]内部属性,保存的是函数创建时当前正在执行的上下文环境,当函数被调用并创建执行上下文时会以[[Environment]]的值初始化作用域链,所以从规范也可以得知函数的作用域只跟函数创建时的当前上下文环境有关。 规范中关于[[Environment]]的描述:https://tc39.es/ecma262/#sec-ecmascript-function-objects

    作者回复: 赞

    2020-06-29
    13
  • 零维
    老师,请问一下我下面关于作用域的理解是否正确: 如果我运行一个 js 文件,在解释阶段生成 AST 树之后,紧接着,这个 js 文件的所有的作用域(函数作用域,块级作用域,全局作用域)就都已经确定了,就算有某些函数没有被执行,它的作用域内含有哪些变量也已经确定了,但是这些变量还都不会真实存在栈或堆中。也就是说,某个未执行函数的执行上下文中的变量环境和词法环境现在也已经确定了。 这样的理解对吗?

    作者回复: 正常情况下是这样的,单是执行eval的情况,这个eval方法很有破坏性,因为在执行eval之前,引擎并不知道eval要执行的内容,也就没有办法提前做预解析

    2020-05-19
    4
  • 离人生巅峰还差一只猫🐈
    老师你好,有个问题想请教下: 之前提到在编译阶段就会生成作用域和AST,在本节中又提到函数在执行时才会创建作用域。那么编译时创建的作用域具体是哪些作用域,因为通过d8 print-scopes发现都所有作用域都存在

    作者回复: 是打印所有的作用域,编译的时候就编译什么代码就创建什么代码的作用域。 比如执行全局代码的时候,只会生成全局作用域,函数的作用域就不会被生成,当执行某个函数时,就会生成函数的作用域了。

    2020-06-30
    3
  • 于你
    之前看《浏览器运行原理》时候,说的是作用域链是在定义代码的时候决定的,当时的有点迷糊,今天突然看明白了,看来还是需要反复巩固知识啊,给老师点个赞!

    作者回复: 👍

    2020-04-09
    2
  • chengl
    老师你好,为了更好的理解效果,建议有打印信息的代码,在代码底部增加打印结果,便于结合理解,有些代码虽然很简单,但是就怕理解错了。

    作者回复: 好,回头我修订的时候我来补充

    2020-04-09
    1
  • zlxag
    老师你这个图片怎么画的呀

    作者回复: keynote

    2020-04-03
    2
    1
收起评论
显示
设置
留言
31
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部