VO AO 的解释 https://www.cnblogs.com/songjum/p/4545800.html
1. 作用域
ES5中只有全局作用域和函数作用域,我们都知道他没有块级作用域。
ES6中多了一个let,他可以保证外层块不受内层块的影响。即内层块形成了一个块级作用域,这是let的一个特点。
var a=1; function f1(){ var b=2; function f2(){ var c=b; b=a; a=c; console.log(a,b,c); } f2(); } f1();//2,1,2
上面的代码,有三个执行上下文环境(EC),全局EC,f1EC,f2EC。全局环境下有一个变量a和一个函数f1(),在f1环境中,有一个变量b和一个函数f2(),在f2环境中有一个变量c。但在f2中,可以访问到f1环境中的b,也可以访问到全局环境中的a,在f1中,可以访问到全局环境下的a,但不可以访问f2中的c,在全局中,不可以访问f1中的b也不可以访问f2中的c。这就是一个作用域链。
函数的内部环境可以通过作用域链访问到所有的外部环境,但是外部环境却不可以访问外部环境,这就是作用域的关键。但是我们要知道,作用域是在一个函数创建时就已经形成的,而不是调用时。
var a=10; function fn(){ var a=20; return function b(){ console.log(a); }; } var g=fn(); g();//20
此图说明了作用域链向上查找是寻找创建它的那个作用域。
2. 闭包
“闭包,允许使用内部函数(即函数定义和函数表达式位于另一个函数的函数体内),而且,这些内部函数可以访问他们所在的外部函数中的声明的所有局部变量丶参数和声明的其他内部函数,当其中一个这样的内部函数在包含他们的外部函数之外被调用时,就会形成闭包。即内部函数会在外部函数返回后被执行。而当这个内部函数执行时,它仍然必须访问其外部函数的局部变量丶参数以及其他内部函数。这些局部变量丶参数和函数声明(最初时)的值是外部函数返回时的值,但也会受到内部函数的影响。”
简单来说,就是在一个函数a内部定义的另一个函数b,当b在a之外被执行时,就会形成闭包。同时b函数仍然可以访问到a函数中的局部变量与函数。
function fn(){ var array=[]; for(var i=0;i<10;i++){ array[i]=function(){ return i; } } return array; } fn();//[ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ]
闭包保存的是定义它的那个函数内部的局部变量丶参数和其他内部函数,也就是说保存的是这个函数执行上下文中的整个VO,而不是一个变量。上面代码中的函数作用域链中都保存着fn的活动对象,他们引用的都是一个i,当fn返回时,i的值是10,所以每个函数都引用保存i那个变量的同一个变量。我们如果想得到原先想得到的那个结果,可以加上另一个匿名函数改变他的父作用域(其实应该是创建它的作用域),将它包裹起来。
function fn(){ var array=[]; for(var i=0;i<10;i++){ array[i]=function(num){ return function(){ return num; }; }(i); } return array; }
这个匿名函数有一个参数num,同时是返回值。在调用每个匿名函数时,传入了变量i。由于参数是按值传递的,所以i就会复制给num,而这个匿名函数的内部又创建了一个访问num的闭包,返回后能够访问到该匿名函数中的VO变量对象(Variable Object)(包括参数),于是每个函数返回的都是num的一个副本,所以可以得到不同的值。
闭包的两个场景;
1. 函数作为函数的返回值
function f(){ var a=1; return function(){ console.log(a); } } var g=f(); g();//1;
2. 函数作为参数传递
function f(){ var a=1; return function(){ console.log(a); } } var g=f(); g();//1; function F(fn){ var a=2; fn(); } F(g);//1
上面两个小例子也正好说明了闭包可以访问定义它的那个函数作用域下的内部变量和内部函数。其实是整个VO变量对象(Variable Object),所以还包含参数。