浅谈JavaScript作用域,作用域链,预编译

之前写过一篇关于预编译和作用域的博客,最近更深入的去了解了一下,发现写的真是屎,这次算上作用域链从新纪录一下我自己的理解。(如果这篇文章有什么问题请及时联系我)!

预编译

js是一种解释型语言,解释一句执行一句。
js运行有三步
一.语法检测:是否有语法问题?
二.预编译
变量和函数的提前声明
全局预编译
1. 创建一个GO对象 Global Object
2. 将var关键字声明的变量当作GO对象的属性,赋值为undefined,有重名的直接覆盖
3. 将function关键字声明函数 当作GO对象的属性,值为函数体,重名直接覆盖
函数预编译
函数执行的前一刻开始
4. 创建一个AO对象 Activation Object 执行期上下文对象
5. 函数的形参,成为AO对象的属性,值为实参的值,若未传值,值为undefined
6. 将var关键字声明的变量,成为AO对象的属性,值为undefined,遇到已经存在的,不做任何变化
7. 将function声明的函数 成为AO对象的属性 值为函数体,重名直接覆盖

三.代码执行
预编译的例图:

浅谈JavaScript作用域,作用域链,预编译_第1张图片

作用域

作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性
在es6之前只有全局作用域以及函数作用域,今天我们不考虑es6的let和const。

  1. 全局作用域
    写脚本块中的代码
    全局作用于中声明的变量,会被提前到代码块的顶部进行定义,成为全局对象的属性
  2. 函数作用域
    函数内部定义的变量,会被提升到函数代码块中的顶部,并且不会成为全局对象的属性

作用域链

在js代码进行预编译的时候生成的GO以及AO对象,形成了一种链式结构。
提到作用域链那就要说一下函数的[[scope]]属性。
[[scope]] :执行期上下文对象集合,
也就是说你生成的GO和AO对象被保存在每个函数的[[scope]]里(但是我觉得指向这个对象的地址更准确些

function a(){
            function b(){
                var bb = 234
            }
            var a = 123;
            b();
        }
        var glob = 100;
        a();

像上面的这段代码,在进行预编译时首先生成了全局的GO对象保存了处于全局对象的变量以及函数,

函数a()进行定义时,a.[[scope]]的集合里保存了GO对象(第0项),在函数执行时创建了a的Ao对象并保
存a里定义的变量和函数,然后将AO对象存入a.[[scope]]里,此时第0项为a的Ao对象,第1项为GO对象。

函数b()进行定义时,b.[[scope]]的集合里保存了a的Ao对象(第0项)和GO对象(第1项),在函数执行时创建了b的Ao对象并保
存b里定义的变量和函数,然后将b的AO对象存入b.[[scope]]里,此时第0项为b的Ao对象,第1项为a的Ao对象,第2项为GO对象。

那么这个是怎么用的呢?
如果我在b函数里想使用一个变量,而b()函数没有这个变量,那么他就会从自己的第0项开始找,一直向后找,直到找到全局的G0对象。
这些G0和AO对象形成了一种链式结构,这就是作用域链。

具体如下图:

浅谈JavaScript作用域,作用域链,预编译_第2张图片

你可能感兴趣的:(js学习,javascript)