小白必看——作用域与作用域链

306 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。


前言

作为JavaScript初学者,我们首先要了解的一个概念就是作用域。诺没有作用域这个概念,程序虽然也能执行一些简单的任务,但是程序会受到高度限制,不能实现更高级的编程。


作用域的概念

那作用域是什么呢?用丐版语言来描述的话,就是能够储存变量当中的值,并且能在之后对这个值进行访问和修改。程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。


作用域的三大类型

  1. 全局作用域
  • 指代定义在全局的变量形成的作用域
  1. 函数局部作用域
  • 指代定义在函数体内的变量形成的作用域
  1. 代码块级作用域
  • 指代用let,const定义的变量

词法作用域

代码在执行之前需要被编译,编译的过程:

  1. 词法化 (创建词法作用域)
  • 父级作用域是不能取子集作用域的字符的,而子集作用域是可以调用父级作用域的字符
  • 执行阶段查找作用域是由内而外 (找到第一个之后就停止查找 --- 遮蔽效应)
  • 全局变量会变成window对象上的属性
  1. 欺骗词法作用域(欺骗词法)
  • eval() 让原本不属于这里的代码,好像天生就写在了这里一样
function foo(str,a){ 
    eval(str) --'var b = 3'就像原本就写在这里 
    console.log(a,b);
    } 
    var b = 2 
    foo('var b = 3', 1)
    输出结果( 1 , 3 )

当给foo函数加入一个eval()时,eval就会将传进来的形参str转化成原本定义在函数的变量,用来欺骗函数,使函数用这个变量去执行。

  • with可以修改对象中属性的值
  • 但是要注意的一点就是,当使用with修改一个对象中不存在的属性时,那么这个属性会被泄漏到全局
var obj = { 
    a: 1
    b: 2
    c: 3
}

with(obj) {
    a = 2 
    b = 3
    c = 4
}

作用域链

首先先介绍三个概念

  1. 执行期上下文:
  • 当函数执行的时候,会创建一个成为执行其上下文的内部对象(AO对象)。
  • 一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,
  • 所以多次调用一个函数会导致创建多个执行期上下文,当函数执行完毕,它所产生的执行期上下文会背销毁。
  1. 查找变量 从作用域链的顶端依次往下查找。
  2. [[scope]] [[scope]是函数的作用域,是不可访问的,其中存储了运行期上下文的集合。

作用域链概念

作用域链指代的就是在[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。用大白话来讲,就是在函数执行时,将产生的作用域用链表的方式连接,再按照下标顺序依次执行。

function a(){

    function b(){

        var b = 22
    }
    var a = 111

    b()
    console.log(a);
}
var glob = 100
a()

来看这段代码,首先定义了函数a,生成了a[[scope]] -->0:GO{},紧接着执行函数a,生成了a[[scope]] -->0: AO{} 1:GO{},但在函数a执行的过程中遇到了函数b,又定义了b[[scope]] -->0: bAO{} 1:aAO{} 2:aGO{},形成了成链表形式排列的三个作用域,之后就会依照链表的顺序以此执行。


总结

在这块知识点中,我们要先了解作用域的基本概念、类型、以及产生的原理,同时掌握欺骗词法作用域的用法。再去深入的理解作用域链的概念和原理即可。