什么是函数作用域和全局作用域?
每个函数在执行时都需要查找自己的作用域,我们称为函数作用域,在执行阶段,在执行一个函数时,当该函数需要使用某个变量或者调用了某个函数时,便会优先在该函数作用域中查找相关内容。
参考一段代码:
var x = 4
var test
function test_scope() {
var name = 'foo'
console.log(name) // 'foo'
console.log(type) // undefined
console.log(test) // 1
var type = 'function'
test = 1
console.log(x) // 4
}
test_scope()如果在当前函数作用域中没有查找到变量,那么V8会去全局作用域中去查找,这个查找的线路就称为作用域链。
全局作用域和函数作用域类似,也是存放变量和函数的地方,但是它们还是有点不一样: 全局作用域是在V8启动过程中就创建了,且一直保存在内存中不会被销毁的,直至V8退出。而函数作用域是在执行该函数时创建的,当函数执行结束之后,函数作用域就随之被销毁掉了。
全局作用域中包含了很多全局变量,比如全局的this值,如果是浏览器,全局作用域中还有window、document、opener等非常多的方法和对象,如果是node环境,那么会有Global、File等内容。
作用域链是怎么工作的?
回到开始的代码。
V8启动之后,消息循环系统便开始工作了,这时候,我输入了这段代码,让其执行。
V8会先编译顶层代码,在编译过程中会将顶层定义的变量和声明的函数都添加到全局作用域中,最终的全局作用域如下图所示:
全局作用域创建完成之后,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 = '极客时间'
type = 'global'
bar()
第一部分是在编译过程中完成的,此时全局作用中两个变量的值依然是undefined,然后进入执行阶段;第二部代码就是执行时的顺序,首先全局作用域中的两个变量赋值“极客时间”和“global”,然后就开始执行函数bar的调用了。
当V8执行bar函数的时候,同样需要经历两个阶段:编译和执行。在编译阶段,V8会为bar函数创建函数作用域,最终效果如下所示:
然后进入了bar函数的执行阶段。在bar函数中,只是简单地调用foo函数,因此V8又开始执行foo函数了同样,在编译foo函数的过程中,会创建foo函数的作用域,最终创建效果如下图所示:
好了,这时候我们就有了三个作用域了,分别是全局作用域、bar的函数作用域、foo的函数作用域。
foo函数查找变量的路径到底是什么?
foo函数作用域—>全局作用域
因为JavaScript是基于词法作用域的,词法作用域就是指,查找作用域的顺序是按照函数定义时的位置来决定的。bar和foo函数的外部代码都是全局代码,所以无论你是在bar函数中查找变量,还是在foo函数中查找变量,其查找顺序都是按照当前函数作用域–>全局作用域这个路径来的。
可以参考下图:
有两点需要注意:
因为词法作用域是根据函数在代码中的位置来确定的,作用域是在声明函数时就确定好的了,所以我们也将词法作用域称为静态作用域。
和静态作用域相对的是动态作用域,动态作用域并不关心函数和作用域是如何声明以及在何处声明的,只关心它们从何处调用。换句话说,作用域链是基于调用栈的,而不是基于函数定义的位置的。
此文章为5月Day13学习笔记,内容来源于极客时间《图解 Google V8》,日拱一卒,每天进步一点点💪💪
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net