目录
1.作用域(Scope)定义
[[scope]]:每个javascript函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供javascript引擎存取,[[scope]]就是其中一个。[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。即作用域决定了代码区块中变量和其他资源的可见性。
2.变量的作用域类型
1.全局作用域:全局作用域贯穿整个javascript文档,在所有函数声明或者大括号之外定义的变量,都在全局作用域里。一旦你声明了一个全局变量,那么你在任何地方都可以使用它,包括函数内部。
2.局部作用域:局部作用域一般只在固定的代码片段内可访问到,而其外部是无法访问的,它分为函数作用域和块级作用域
a:函数作用域: 当你在函数里定义一个变量时,它在函数内任何地方都可以使用。在函数之外,你就无法访问它了。
b:块级作用域:你在使用大括号时,声明了一个const
或者let
的变量时,你就只能在大括号内部使用这一变量。
3.作用域链
作用域链:[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。
4.执行期上下文
当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,执行上下文被销毁。(即AO、GO,不了解的可参考预预编译文章)
5.查找变量
在函数中查找变量,从其作用域链的顶端依次向下查找,从Scope chain 的0位依次查到最后一位。
6.例1
function a() {
function b() {
var bb = 234
}
var aa = 123;
b();
}
var glob = 100;
a();
1.a()定义时,其Scope里存放GO
2.a()执行时,形成作用域链,Scope chain[0] 存放AO, Scope chain[1] 存放GO
3: b()定义,因为它是a()内部的函数,因此其执行期上下文与a()执行时是一样的;
4:b()执行时,产生自己的AO,在作用域链中将a()的AO与GO储存位置下移,第0位存自己的AO
注意:b() Scope chain 储存的a() 的AO与a()中的AO一样,只是将其引用挂到里面,即通过b()中可以改变a()中变量的值
下面这个例子输出 a 为0;
function a() {
function b() {
var bb = 234;
aa = 0;
}
var aa = 123;
console.log(a);
}
var glob = 100;
a();
7.例2
先看代码:
function a(){
var aa = 123;
function b(){
var bb = 456;
function c(){
var cc = 789;
}
c();
}
b();
}
a();
这里的调用生成AO,GO顺序为:
a defined(定义)a.[[scope]] 0:GO
a doing(执行) a.[[scope]] 0:AO(a) //a的AO
1:GO
b defined(定义)b.[[scope]] 0:AO(a)
1:GO
b doing(执行) b.[[scope]] 0:AO(b)
1:AO(a)
2:GO
c defined(定义)c.[[scope]] 0:AO(b)
1:AO(a)
2:GO
c doing(执行) c.[[scope]] 0:AO(c)
1:AO(b)
2:AO(a)
3:GO
8.闭包的引入
看下面这个例子:
function a(){
function b(){
var bbb = 234;
console.log(aaa);
}
var aaa = 123;
return b;
}
var glob = 100;
var demo = a();
demo();
打印是123;
这里 demo = a();当 a() 执行完,一般来说会销毁创建 a 的 AO对象,但是 a() 返回的是 b(),所以没法销毁 a 的 AO对象,因为 b()的作用域链还链着 a 的 AO 对象。
所以执行 console.log(aaa) 的时候,先访问 b 的AO对象,没有 aaa,所以再访问 a 的AO对象,打印123;
这个过程称为 闭包;
但凡内部的函数被保存到了外部,一定产生闭包