我们对js的日常认知是否是因为自己眼睛所看见的代码从而执行呢?例如:下面这段简单代码,大家肯定都知道答案。那么大家知道这是为什么吗?下面我就给大家做一个详细的解答,输出10的原理到底是什么!那就是我们所要了解的作用域了!
var aa = 5;
function a (){
var aa = 10;
console.log(aa);
}
a();//10
1.作用域 [[scope]]
每个javascript函数都是一个对象, 对象中有些属性我们可以访问,但有些不可以,这些属性仅供javascript引擎存取,[scope]]就是其中一 个。[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。
作用域链: [[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链
上面就是作用域的解释,但我知道大家看不懂,下面我给大家重新讲解一下。我们所说的作用域。
当一个函数,它被定义的时候,记住!当被定义时候,那么它的作用域就生成了!叫做[[scope]],例如:
var aa = 5;
function a (){
var aa = 10;
console.log(aa);
}
a.[[scope]] 那么此时,a的作用域形成了,那他里面有什么呢,让我给大家来说明:
[[scope]指的就是我们所说的作用域,其中存储了运行期上下文的集合(GO,AO)。函数的作用域取决于函数所处的位置,
此时a函数在window中,所以在a的作用域就链着GO,也就是全局作用域。a.[[scope]]里面是键值对形式,一开始的键值对是0:GO
a.[[scope]] ———> 0 : GO{ aa : 5, a : function a (){}}
如果此时,我们来执行 a 函数,那么它的作用域就会发生变化!
var aa = 5;
function a (){
var aa = 10;
console.log(aa);
}
a();// 10
因为在函数执行的前一刻会生成函数的 运行期上下文,简称AO对象,所以此时,我们的作用域中就会多出
一个AO对象(下面所写为函数执行时的AO),然后将GO往下挤,所以第0位就变成了a的AO
a.[[scope]] ———> 0: AO{ aa : 10}
———> 1: GO{ aa : 5, a : function a (){}}
所以我们输出的答案aa是10,因为我们优先再我们自身作用域中寻找aa,如果没有再向下一层(上一级)寻找aa。(也就是GO对象中)
这才是我们真正输出10的原理。
这就是最最基本的函数作用域!(如果有不知道GO对象 和 AO对象的是什么 或者不知道 如何生成的,可以参考我上篇博客,预编译!)
2.作用域链
上面我们就简单了解了什么是函数作用域,和作用域叫什么,它里面存放的是什么。下面我就给大家讲解一下,什么叫做作用域链:
var aa = 55;
function a() {
var aa = 100;
function b() {
aa++
console.log(aa)
}
b()
}
a() // 101
根据我们上方对作用域的了解,我来给大家画一张作用域链的图,只要可以有助于大家更好的理解:
首先是我的a函数定义:
此时a函数在window中,所以在a的作用域中有着GO,也就是全局作用域。所以:
a.[[scope]] ———> 0: GO{ aa : 55, a : function a (){}}
紧接着就是a函数的执行:自己生成AO会将GO往下挤
a.[[scope]] ———> 0: aAO{ aa : 100, b :function (){} }
———> 1: GO{ aa : 55, a : function a (){} }
当a函数执行时,那么b函数就被定义了!当b函数定义时,它是站在a函数中,所以b函数的作用域在生成的时候,是站在a函数的肩膀上,所以他就有着a函数的AO和GO
b.[[scope]] ———> 0: aAO{ aa : 100, b :function (){} }
———> 1: GO{ aa : 55, a : function a (){} }
当随着代码的执行,b函数也开始执行:b函数就会生成一个属于自己的AO对象:自己生成AO会将a函数的aAO 和 GO 往下挤
b.[[scope]] ———> 0: bAO{}
———> 1: aAO{ aa : 100, b :function (){} }
———> 2: GO{ aa : 55, a : function a (){} }
然后执行我们的b函数代码,a++,我们先在我们的b函数自身的AO对象中寻找a变量,但是没有找到,所有就往上层查找,就在aAO中找到了aa,然后使用修改了这个aa的值,并且输出。
当我们的b函数输出完毕后,就代表了b函数执行完毕,当函数执行完毕后它就是放弃自己生成的bAO对象,随着a函数执行完毕,a函数也执行完毕,它也会放弃自己这次生成的aAO对象,随着aAO对象消失,b函数就不见了,那么b函数的作用域也就会消失,最后只剩下a函数的作用域。
这便是最最简单的作用域链形成和运用!
谢谢大家的浏览~~