本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
作为JavaScript初学者,我们首先要了解的一个概念就是作用域。诺没有作用域这个概念,程序虽然也能执行一些简单的任务,但是程序会受到高度限制,不能实现更高级的编程。
作用域的概念
那作用域是什么呢?用丐版语言来描述的话,就是能够储存变量当中的值,并且能在之后对这个值进行访问和修改。程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
作用域的三大类型
- 全局作用域
- 指代定义在全局的变量形成的作用域
- 函数局部作用域
- 指代定义在函数体内的变量形成的作用域
- 代码块级作用域
- 指代用let,const定义的变量
词法作用域
代码在执行之前需要被编译,编译的过程:
- 词法化 (创建词法作用域)
- 父级作用域是不能取子集作用域的字符的,而子集作用域是可以调用父级作用域的字符
- 执行阶段查找作用域是由内而外 (找到第一个之后就停止查找 --- 遮蔽效应)
- 全局变量会变成window对象上的属性
- 欺骗词法作用域(欺骗词法)
- 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
}
作用域链
首先先介绍三个概念
- 执行期上下文:
- 当函数执行的时候,会创建一个成为执行其上下文的内部对象(AO对象)。
- 一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,
- 所以多次调用一个函数会导致创建多个执行期上下文,当函数执行完毕,它所产生的执行期上下文会背销毁。
- 查找变量 从作用域链的顶端依次往下查找。
- [[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{},形成了成链表形式排列的三个作用域,之后就会依照链表的顺序以此执行。
总结
在这块知识点中,我们要先了解作用域的基本概念、类型、以及产生的原理,同时掌握欺骗词法作用域的用法。再去深入的理解作用域链的概念和原理即可。