【每日一题】说下你对变量的作用域链的理解
共 1754字,需浏览 4分钟
·
2021-09-06 18:34
人生苦短,总需要一点仪式感。比如学前端~
全局变量和局部变量
“全局变量”
指的是定义在所有函数之外的变量(也就是定义在全局代码中的变量)
“局部变量”
与之相对,所指的是在某个函数中定义的变量。
其中,函数内的代码可以访问自己上层函数的变量,也可以访问全局变量,这样就构成了作用域链。
特殊的:隐式声明一个变量(没有使用 var 等语句就声明的变量),该变量不管在哪个作用域里,就会被默认为是全局变量。
作用域链
JavaScript 引擎执行一段代码时,会创建对应的执行上下文并推入到执行栈中。
查找一个变量的时候,会先从当前代码所在的上下文的环境记录中查找,
如果找到,直接返回这个变量的值;
如果没有找到,就会到上一级执行上下文的环境记录中查找,找不到会一直向上,直到全局上下文的环境记录。
这样有多个执行上下文的环境记录构成的链表,就叫做作用域链
。
当前执行上下文 - 上一级 - 上上级 - …… - 全局上下文
。
作用域链
包含了执行环境有权访问的变量、函数的有序访问。它是一个由变量对象(VO/AO)组成的单项链表,主要用来进行变量查找。
js 内部有一个[[scope]]
属性,这个属性就是指向作用域链的顶端
var val = "全局变量";
function AA(y) {
var val = "局部变量";
function BB() {
var z = 0;
alert(val);
}
BB();
}
AA(5);
简单分析上面的流程:
全局执行环境:[[scope]] ---> VO[AA,val]
,只有全局VO, [[scope]]直接指向VO。
函数 AA 执行环境:[[scope]] ---> VO[y,BB,val, VO[AA,val]]
,首先全局VO压入栈底,然后函数AA VO压入栈顶,[[scope]] 属性指向栈顶,变量、函数搜索就从栈顶开始。
函数 BB 执行环境:[[scope]]---> VO[z, VO[y,BB,val], VO[AA,val]]
,首先全局 VO 压入栈底,然后依次 AA、BB 压入栈,BB 处于栈顶,[[scope]]属性直接指向 BB 的 VO。
应用场景:比如调用 BB,进入 BB 的执行环境,在执行 alert 的时候,首先会去查找 val 的声明,会先在作用域链的顶端查找,没查到就会沿着作用域链继续往下查找,直到查找到AA的变量对象就停止。
总结:
函数执行的时候,就将当前函数的VO放在链表开头,后面依次是上层函数,最后是全局对象。变量查找则依次从链表的顶端开始。
js有个内部[[scope]]
,这个属性包含了函数的作用域对象的集合,这个集合就称为函数的作用域链。它决定了哪些变量或者函数能在当前函数中被访问以及它的访问顺序。
VO 和 AO
VO
:全局变量对象(Varibale Object) , 指向全局对象window,包含定义的全局变量。AO
:活动变量对象(Activation Object),其实也是变量对象,可以理解为VO在函数中的叫法。不过他除了包含局部的变量,还包括函数内部的形参、arguments对象、this对象等。
区分作用域与this
变量的作用域区别于 this指针:变量作用域
是静态的,在变量声明后就确定的,也就是说变量声明在哪里,他的作用域就是哪里(特殊一点:没有用关键词声明的是全局);
而 this指针
则是动态的,根据最后的调用情况判断其指向谁,甚至根据call、apply、bind、new等影响能被手动改变。