JavaScript —— AO、GO、作用域链

2,022 阅读5分钟

写在前面

为什么要深入学习JavaScript基础?

JavaScript 堪称世界上被人误解最深的编程语言。虽然常被嘲为“玩具语言”,但在它看似简洁的外衣下,还隐藏着强大的语言特性。 JavaScript 目前广泛应用于众多知名应用中,对于网页和移动开发者来说,深入理解 JavaScript 就尤为必要。源文地址

个人心得:

  1. 源码阅读与学习受阻(更深入了解框架的运作方式,后期学习与使用框架会有事半功倍的效果,学习写代码设计思想思维方式,学习新的框架更容易触类旁通)
  2. 个人能力提升受阻(基础决定了你的职业道路能走多远,甚至是解决问题的速度有多快,方法有多好)

GO、AO、预解析

导语

本文将主要以习题实战的方式让大家有更透彻的理解,下面开始今天的学习:

GO、AO、预解析究竟是什么? 在了解这些之前我们先来看看JavaScript语言的执行过程,大致分为下面几个步骤:

  1. 语法检查:对整体代码进行词法分析,语法分析
  2. 代码预解析:生成GO、AO对象(也称作预编译)
  3. 解释执行:逐行运行代码

注意: 在javascript解析过程中,如果遇到错误,会直接跳出当前的代码块,直接执行下一个script代码段,因此在同一个script内的代码段有错误的话就不会执行下去。但是它不会影响下一个script内的代码段。

了解了JavaScript执行过程,下面我们来看看对于这三个关键词的简单解释:

  • GO
    • 全局执行上下文
    • Global Object 全局作用域
  • AO
    • 函数执行上下文
    • Activation Object 局部作用域
    • 当一个方法被 调用 的时候(执行之前)会形成一个局部作用域AO
  • 预解析
    • 函数执行之前需要执行的一个步骤

看了这么多文字,你可能无法理解,没关系,我们直接进入实战练习,看看他们的执行过程是怎样的。

GO

  1. 寻找变量声明

  2. 寻找函数声明并赋值

  3. 执行

    代码示例:

    1. console.log(a)
    2. var a = 1
    3. function a() {
    4.   console.log(2)
    5. } 
    6. console.log(a)
    

JavaScript解析器在执行这段代码之前,首先进行词法分析(上面代码没有错误,直接通过),接着进入预解析环节,生成GO对象,GO对象内包含默认属性例如:window: Object,this: window(浏览器环境下),最后再执行改代码!

所以在第二步预解析生成GO对象的时候,是这样的

GO
thiswindow
window(object)
......

下面我们写出他的详细过程:

  1. 寻找变量声明:(var关键字)

    2. var a = 2;
    

    找到第二行代码,并提升var a ,此时的GO对象如下:

    GO
    thiswindow
    window(object)
    aundefined
    ......

    可以看到GO对象内部多了一个a属性,值为undefined

  2. 寻找函数声明(function关键字)

    3. function a() {
    4.   console.log(2)
    5. } 
    
    GO
    thiswindow
    window(object)
    afunction a() {...}
    ......
    • 此时的a 从 undefined 变成了 function
  3. 执行

    1. console.log(a) // function(){...}
    2. a = 1  // a = 1, 此时GO内的a属性的值由function改为了1
    6. console.log(a)  // 1
    程序执行完毕
    

    下面是整个GO对象变化的过程

    GO
    thiswindow
    window(object)
    aundefined --> function a(){...} --> 1
    ......

至此,对于GO,相信你有了一定的了解,AOGO 执行方式相似,下面我们继续看 AO

AO

AO的执行过程分为如下四个步骤:

  1. 寻找形参和变量声明
  2. 实参值赋值给形参
  3. 找函数声明
  4. 执行

继续看代码:

1. var a = 1
2. function test(c){
3.   var b = 2
4.   console.log(a)
5.   console.log(b)
6.   console.log(c)
7. }
8. test(3)

预编译阶段,GO生成(省略初默认字段)

GO
aundefined
testfunction () {...}

生成AO如下:

AOfunction test
cundefined
bundefined

接着运行代码:

1. var a = 1 // GO.a = 1
7. test(3)  // 

执行test(3)

3. b = 2  // AO.b = 2
4. console.log(a) // 1, 当前ao没有a属性,向上查找 GO.a,具体逻辑查看后面的作用域链
5. console.log(b) // 2, AO.b
6. console.log(c) // 3, AO.c

注意:AO只有在函数执行前才会生成AO对象,未被调用的函数不会生成AO对象,test函数执行完毕,AO对象在没有被其他地方引用的情况下,会被立即销毁。

趁热打铁,快速写出下面代码AO对象

function test(a){
  console.log(a);
  var a = 1;
  console.log(a);
  function a(){}
  console.log(a);
  var b = function(){}
  console.log(b);
  function d(){}
}

test(2);
GO
testfunction(){...}
AO
aundefined -> function(){...} --> 1
bundefined --> function(){...}
dfunction(){...}

上面代码运行结果

function test(a){
  console.log(a);   // function a(){}
  var a = 1;
  console.log(a);   // 1
  function a(){}
  console.log(a);   // 1
  var b = function(){}
  console.log(b);   // function b
  function d(){}
}

test(2);

下面是练习

function test(){
  a = 1;
  function a(){}
  var a = 2;
  return a;
}
console.log(test());
function test(){
  console.log(a);
  var a = b = 3;
}
test();
console.log(b);
function test(){
  console.log(b);
  if(a){
    var b = 3;
  }
}
test();
var a = 4;
function test(){
  return b;
  var b = 3;
}
test();
var a = 4;
console.log(fun1);
console.log(fun2);
function fun1() {
  console.log('函数fun1');
}
var fun2 = function () {
  console.log('函数fun2');
}
console.log(fun1);
console.log(fun2);
var a = 2
function test(b) {
  console.log(a)
  var  a 
  a = 3
  a = 4
  console.log(a) 
}
test(2)
GO
thiswindow
window(object)
aundefined --> 2
testfunction test(){...}
AO
thiswindow
window(object)
bundefined
aundefined --> 3 --> 4
testfunction test(){...}

AO、GO 实战练习

function a(b) {
  console.log(b); 
  function b(){
    console.log(b); 
  }
  b();
}
a(1)
function a(b){
  console.log(b)
  var b = function(){
    console.log(b)
  }
}
a(2)
console.log(fun);
function fun(fun){
  console.log(fun);
  var fun = 2;
  console.log(fun); 
  function fun(){}
}
fun(1);  
var fun = 1;

碰到return语句

function test(){
  return a;
  a = 1;
  function a(){}
  var a = 2;
}

碰到if语句

function test(e){
  console.log(e);
  function e(){}
  arguments[0] = 2;
  console.log(e);
  if(a){
    var b = 3;
  }
  var c;
  a = 4;
  var a;
  console.log(b);
  f = 5;
  console.log(c);
  console.log(a);
}
var a;
test(1);
console.log(a);
console.log(f);

作用域链

AO/GO

  • AO:函数执行期上下文
  • GO:全局执行期上下文 (函数执行完成之后AO会被销毁,再次执行AO会重新生成)

作用域链

  • 用于保存这些上下文的一个容器

scope

  • 函数创建时,生成的一个JS内部的隐式属性。
  • 函数存储作用域链的容器
function a(){
    function b(){
        var b = 2;
    }
    var a = 1;
    b();
}
var c = 3;
a();

Image1.png

function test1(){  
    function test2(){}  
    var a = 1return test2;
}
var c = 3;
var test3 = test1();
test3();

Image2.png