小编上一篇博客所介绍的预编译问题,主要是给大家强调js中AO、GO对象,而这所有的都是为了闭包做一个铺垫,经历了一天的倒腾跟进化了一样,话不多说开始今天的分享,每一个知识点都会有实际案例,希望对大家有帮助。
一、作用域链scope
在介绍闭包之前我们要了解这样的一个东西——作用域链,在js代码执行时,所产生的的AO、GO对象存储在一个作用域链之中,我们把scope想象成一个数组每一个函数运行所产生的对象以入栈的方式存储在其中,那么当函数运行时就是在作用域链头往后去寻找对应值,且当函数运行完后会丢弃其本身AO,所有函数共用一个作用域链。
案例:
function a(){
var aa=123;
function b(){
console.log(aa);
}
b();
}
a();
那么答案当然很明显是返回aa的值但其中所经历的过程较为复杂(这边只是将scope看做数组方便理解实际不是):
a 执行> scope →0:a的AO 1:GO
b 执行> scope →0:b的AO 1:a的AO 2:GO (那么b执行访问操作依次寻找到a:AO 中找到结果输出)
b 执行结束> scope →0:a的AO 1:GO
a 执行结束> scope →0:GO (GO全局对象显然一直存在)
强调:小编这边特地是用的箭头我们将scope理解为一个固定区域,而执行函数时向他添加对象,寻找对象时指向他去索引,函数执行完同样在其中将自身内容消除。
二、闭包
概念:将内部函数保存到外部时会产生闭包
闭包基于作用域链的知识操作。
案例1:
function test(){
var sum=100;
function a(){
sum++;
console.log(sum);
}
function b(){
sum--;
console.log(sum);
}
return [a,b];
}
var arr=test();
arr[0]();
arr[1]();
· 这个案例是一个很典型的一个闭包,闭包类型的题目不能想当然还是一步一步去分析,当内部函数被返回到外部时产生闭包。当有闭包产生时,scope链不会因为其“父亲”函数的执行完而销毁自身AO,所以案例中函数a、函数b索引的是test函数执行时的作用域链,所以执行时所修改的是相同且访问得到的sum。
三、立即执行函数
当函数只需要被执行一次如果以普通函数来写的话,代码区域就占用了不必要的内存,那么立即执行函数能够实现只执行一次
格式如下:
(function (){}())
立即执行函数除此格式其余与函数功能相同,他同时可以解决一些闭包产生的问题。
重点:大案例(这是闭包一个很鲜明的案例)
function test(){
var arr = [];
for(var i =0; i<10;i++){
arr[i]=function(){
document.write(i);
}
}
return arr;
}
var myarr =test();
for(var j=0;j<10;j++){
myarr[j]();
}
如果执行了大家可能知道输出是是个10,按照语义应该输出0~9。
原因:小编先提醒一点函数的定义代码是首先不会读进去的,函数只有在执行时才会去读取他的定义然后执行代码,所以先将arr仅看做一个存储了函数的数组,在访问时再去看他的定义,那么此时函数访问的 i 就是testAO中的 i 此时 i 经过循环为10,所以函数数组执行相当于输出10个10,那么怎样解决呢?我们采用立即执行函数就可以实现,在函数数组赋予定义的地方包裹一个立即执行函数使每一次定义都被直接执行。
function test(){
var arr = [];
for(var i =0; i<10;i++){
(function (j) {
arr[j]=function(){
document.write(j);
}
}(i));
}
return arr;
}
var myarr =test();
for(var j=0;j<10;j++){
myarr[j]();
}
这样就能实现了。
不仅仅是在js自身中能产生闭包当函数属性被赋值到外部去时也会产生,例如给标签附上onclick属性也算是生成了闭包。