深入理解JavaScript作用域链和预编译

# 深入理解 JavaScript 作用域及其底层逻辑,本文的上篇,配合食用,效果更佳。

1:前置知识

  1. AO (Activation Object) : 当一个函数被调用时,一个新的执行上下文被创建。在函数内部,AO也称为 局部变量对象,它存储了函数的所有局部变量、参数和函数声明。
  2. GO (Global Object) : 这是全局执行上下文的对象,包含了所有全局变量和函数声明。

2:作用域链

在学习JS的预编译之前,我们必须先了解v8执行代码时的编译和执行过程以及明白作用域链的形成过程和变量的访问顺序。

  • 作用域:函数身上的一个属性,每个函数都是一个对象,而[[socpe]]是一个我们不能访问的属性,用于储存函数中的有效标识符。

  • 作用域链:作用域是当前执行上下文中对象的集合,这种集合呈链式连接,我们把这种链关系称之为作用域链。作用域链的主要目的是确保对变量的访问遵循正确的顺序,就是首先在当前作用域内查找,如果没有找到,则向上遍历链,直到找到变量或者到达链的末尾(全局作用域)。

function a(){
    function b(){
        var b=22
    }
    var a = 111
    b()
    console.log(b)
}
var glob = 100
a()

v8引擎在看到这串代码,会执行以下:

  1. a的定义,生成a的作用域,有一个全局的Go{}对象出现,为0号位,记录全局有效标识符,在a的作用域中,可以访问到GO{}

  2. a的执行,创建a的执行上下文,现在0号位为aAO,一号位为Go{}
    此时会有一个a的作用域链:

微信图片_20240608155853.jpg

3:函数a的调用,带来了函数b的声明,在此之前,b作用域链的0号位会指向a的作用域,然后才变为b的函数作用域,此时会有一个b的作用域链:

微信图片_20240608161843.jpg

3:预编译

预编译是指代码在执行前需要进行编译操作,用于确定代码之间的各种关联。在上一篇文章就已经介绍了变量声明,声明提升,现在,我们再简单提提函数提升,整体提升,见下方代码:

test()
function test(){
    var a = 123 
    console.log(a);
}//输出123,函数体声明提升

当然,知道了概念还远远不够,面试官随手写个代码就能难倒我们。接下来,难度升级,先看下列代码,试着回答下面这个代码会输出什么

var a = 1
function fn(a){
    var a = 2
    function a(){}
    console.log(a)
}
fn(3)
  • 发生在全局
  1. 创建GO对象
  2. 找变量声明,将变量名作为GO的属性名,值为undefined
  3. 在全局找函数声明,将函数名作为GO的属性名,值为该函数体

v8开始编译,全局执行上下文为:
GO:{ a:undefind, fn:function(){} }

v8开始执行,全局执行上下文变为:GO:{ a:1, fn:function(){} }

  • 发生在函数体内
  1. 创建一个AO对象
  2. 找形参和变量声明,将形参和变量名作为AO的属性名,值为undefined
  3. 形参和实参统一
  4. 在函数体内找函数声明,将函数名作为AO的属性名,值为该函数体

v8开始编译,函数体内执行上下文为:
AO:{ a:undefined-->3-->function(){} }

v8开始执行,函数体内执行上下文变为:
AO:{ a:2 }

行文至此,相信诸君已经对上述例题有了答案–2

4:综合运用

上面试题

function fn(a){
    console.log(a);//function
    var a = 123
    console.log(a);//123
    function a(){}
    console.log(a);//123
    var b = function(){}//函数表达式 
    console.log(b);//function
    function c(){}
    var c = a
    console.log(c);//123
}
fn(1)

解析:

  • 全局
    GO:{ fn:function(){} }

  • 函数体内

  1. 找形参和变量声明
    AO:{ a:undefined, b:undefined, c:undefined }

  2. 形参实参统一:
    AO:{ a: 1, b: undefined, c: undefined }

  3. 找函数声明:
    AO:{ a: function a(){}, b: undefined, c: function c(){} }

  4. 执行:
    AO:{ a: 123, b: function b(){}, c: 123 }

5:总结

学习知识不能只停留在表面,在面对面试官提问时,以下这些明显是不够用的。

变量声明,声明提升

函数提升,整体提升

内层作用域可以访问外层作用域

外层作用域不可以访问内层作用域

如果诸君在回答这类问题时,能够不停留于表面的知识点,还能和面试官聊聊题目中的全局作用域,函数作用域,作用域链。从底层出发,究其本质的回答清楚,相信诸君能够在一众面试者中脱颖而出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值