js中的作用域和作用域链

89 阅读3分钟

1,作用域是什么?

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

2,全局作用域和函数作用域的区别?

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

3,作用域链是什么?

我们知道,原型链将一个个原型对象串起来,实现对象属性的查找的路径。而作用域链就是将一个个作用域串起来,实现变量查找的路径。

4,V8中查找对象过程?

当需要使用到js对象时,V8会去对应的作用域中查找该对象。可以通过以下代码进行分析:

var name = 'globalName'
var type = 'globalType'
function A(){
    var name = 'AName'
    console.log(name) // Aname
    console.log(type) // globalType
}
function B(){
    var name = 'BName'
    var type = 'BType'
    A()
}
B()

执行上述代码,可以看到打印结果。在A函数中,查找变量时,会从A函数的作用域中查找。当查找不到时,会从全局变量中查找。通过Chrome 的控制台,我们也可以直观感受一下具体的作用域。如下图所示:

image.png

V8的编译和执行阶段

jsV8执行中,分为两个阶段,一个是编译阶段,一个是执行阶段。在编译阶段中会将顶层定义的变量和声明的函数都添加到全局作用域中。在上述代码中,V8会首先将name、type、A函数、B函数。添加到全局作用域中。无论变量的声明在js的哪个位置,就会被在编译阶段放入全局作用域中,这个过程叫做变量提升

//======编译阶段--变量提升=======
var name = undefined
var type = undefined
function A(){
    var name = 'AName'
    console.log(name)
    console.log(type)
}
function B(){
    var name = 'BName'
    var type = 'BType'
    A()
}
//====执行阶段========
name = 'globalName'
type = 'globalType'
B()

此时全局作用中两个变量的值依然是 undefined,然后进入执行阶段;首先全局作用域中的两个变量赋值“globalName”“globalType”,然后就开始执行函数B的调用了。当V8执行 B 函数的时候,同样需要经历两个阶段:编译和执行。在B函数编译阶段,V8 会为B函数创建函数作用域,同样会有两个变量的定义。接着到了B函数的执行阶段,会将nametype赋值。在调用A函数。同样也会有A函数的两个阶段。。。。

这里注意一个问题,那就是A函数是在B函数中调用的,那么为什么寻找变量type的时候,不从b函数的作用域中查找呢?

这个地方,需要引入一个概念,那就是词法作用域也叫静态作用域JavaScript 所采用的作用域机制就是词法作用域,词法作用域是按照代码定义时的位置决定的,与函数的调用位置无关,在上述代码中,A函数是和B函数同样定义在的全局作用域中,所以A函数向上查找的是全局作用域中的变量。与之相对的还有一个动态作用域,动态作用域并不关心函数和作用域是如何声明以及在何处声明的,只关心它们从何处调用,向上查找的是调用该函数的作用域。