🌟 闭包
//循环遍历,给每个按钮都绑定上单击响应函数
var btns = document.getElementsByTagName("button");
for(var i = 0; i< btns.length; i++){
//这样写会有一个问题,btns是一个伪数组,其中并不包含length属性,所以每次循环都是在单独计算长度
};
//可以修改为:
for(var i = 0; length= btns.length; i<length; i++){
//提前提取出length的数值,以免重复计算
var btn = btns[i];
//将btn对应的索引保存在对应的btn上
btn.index = i;
btn.onclick = function(){
alert("这是第"+ (this.index+1)+"个");
}
}; //函数在for循环执行之后才单击执行,所以只会显示for循环执行后的i的数值
//也可以利用闭包
for(var i= 0 length = btn.length; i<length; i++){
(function(i){
var btn = btn[i];
btn.onclick = function(){
alert("这是第"+ (i+1)+"个");
};
})(i); //匿名函数立即调用
};
1.如何产生闭包?
当一个嵌套的内部函数引用了嵌套的外部函数的变量时,就产生了闭包。
function fn1(){
var a = 2;
function fn2(){
console.log(a);
};
};
fn1();
//fn2引用了fn1的变量,所以产生了闭包
2. 闭包到底是什么?
闭包是嵌套的内部函数,即fn2(多数人的理解);闭包包含被引用变量的对象,即fn2中的closure。(少数人的理解)
闭包存在于嵌套的内部函数中。
3. 产生闭包的条件?
函数嵌套+内部函数引用了外部函数的数据(变量/函数)*外部函数要调用才可以产生
function fn1(){
var a = 2;
var b = "abc"; //闭包中不回含有b变量,因为fn2不需要b
function fn2(){ //执行内部函数定义就会产生闭包,不需要调用内部函数,但是一定要调用外部函数
//外部函数不调用无法定义内部函数,一旦调用就会预处理内部函数
console.log(a);
};
fn2();
};
fn1();
4. 常见的闭包
(1)将函数作为另一个函数的返回值
function fn1(){
var a = 1;
function fn2 (){
a++;
console.log(a);
};
return fn2;
};
var f = fn1(); //闭包产生
f();//2
f();//3
//a是局部变量,本来应该在执行fn1的时候产生,在调用结束后就死亡。
//外部函数执行几次,就产生了几个闭包。
//执行外部函数的时候才会去创建内部函数对象,跟内部函数执行几次无关。
(2)将函数作为参数传递给另一个函数调用
function showDelay(msg, time){
setTimeout(function(){
alert(msg);
}, time)
};
showDelay("lele", 2000);
//有嵌套函数,内部函数引用外部变量msg,并且调用了外部函数,闭包产生
5. 闭包的作用
(1)使函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期)
(2)让函数外部可以操作(读写)到函数内部的数据(变量/函数)
function fn1 (){
var a = 1;
function fn2(){
a++;
console.log(a);
};
function fn3(){
a--;
console.log(a);
};
return fn3;
};
var f = fn1();
f(); //0
* 闭包在函数fn1调用时产生,里面只有变量a;当函数执行完毕后,fn3这个变量消失,但是fn3指向的对象并不会消失,因为f保存了该函数的地址值并指向这个对象,只要有变量指向对象,这个对象就不是垃圾对象。
问题:1⃣️ 在函数执行完成后,函数内部声明的局部变量是否还存在?
一般是不存在的,存在于闭包中的变量才会存在。
2⃣️ 在函数外部能够直接访问函数内部的局部变量吗?不能,但是通过闭包可以
6. 闭包的生命周期
产生:在嵌套内部函数定义执行(函数对象产生,未调用)完成时就产生
死亡:在嵌套的内部函数成为垃圾对象时
f = null;//闭包死亡,包含闭包的函数对象成为垃圾对象
function fn1(){
var a = 2;
var fn2 = function(){ //使用变量赋值的方法创建函数则会在这行执行,而不是内部函数定义执行时产生闭包
console.log(a);
};
};
7. 闭包应用 — 定义JS模块
JS模块:具有特定分功能的js文件,将所有的数据和功能都封装在一个函数内部(私有的),只向外暴露一个包含n个方法的对象或函数。
function myModule(){
//私有数据
var msg = "meow meow";
//操作数据的函数
function fn1 (){
console.log("fn1"+msg.toUpperCase());
};
function fn2(){
console.log("fn2"+msg.toLowerCase());
};
//向外暴露对象(给外部使用的方法)
//将两个函数封装在一个对象中,就可以同时暴露两个方法
return {
fn1:fn1;
fn2:fn2;
};
};
var module = myModule();
module.fn1();
(function(){
//私有数据
var msg = "meow meow";
//操作数据的函数
function fn1 (){
console.log("fn1"+msg.toUpperCase());
};
function fn2(){
console.log("fn2"+msg.toLowerCase());
};
window.myModule2 = {
fn1:fn1;
fn2:fn2;
};
};)();//匿名函数自调用
8. 闭包的缺点
(1)函数执行完后,函数内部的局部变量没有被释放,占用内存时间会变长;
(2)容易造成内存泄漏,即由于疏忽或错误造成程序未能释放已经不再使用的内存。
function fn1(){
var arr = New Array[10000]; //使用中括号表示有10000个空位
function fn2(){
console.log(arr.length);
};
return fn2;
};
var f = fn1();
f(); //f一直指向fn1,如果忘记写f=null; 则这块内存一直都被占用着
f = null; // 让内部函数成为垃圾对象---回收闭包
*能不用闭包就不用闭包/ 即使释放
内存溢出:当程序运行需要的内存超过了剩余的内存时,就出现了内存溢出的错误。
内存泄漏:占用的内存没有及时释放。常见的内存泄漏:意外的全局变量;没有及时清理的计时器或回调函数;闭包。
//意外的全局变量
function fn(){
a = 3; //以为是局部变量,其实是window.a全局变量;要使用var关键字
console.log(a);
};
fn();
//启动循环定时器后不清理
var intervalid = setInterval(function(){
console.log(":)");
},1000);
clearInterval(intervalid); //如果不清除则会一直占用内存
9. 面试题
var name = "The Window";
var object = {
name:"My Object";
getNameFunc: function(){
return function(){
return this.name;
};
};
};
alert(object.getNameFunc()()); //"The Window",没有闭包,对象不会形成作用域
var name2 = "The Window";
var object2 = {
name2:"My Object";
getNameFunc: function(){
var that = this;
return function(){
return that.name2; //有闭包,内部函数引用外部函数的变量
};
};
};
alert(object2.getNameFunc()()); //"My Object"
function fun (n,o){
console.log(o);
return{
fun:function(m){
return fun(m,n);
}
};
};
var a = fun(0);
a.fun(1);
//这里产生了新的闭包,因为在执行fun(1,0)的时候外部函数又被调用了一次,但是因为没有变量接收调用的结果,所以闭包产生后又消失了
//如果是var b = a.fun(1); 后面再使用b调用fun(2),那么闭包内容是会变化的
a.fun(2);
a.fun(3); //undefined 0 0 0
var b = fun(0).fun(1).fun(2).fun(3); // undefined 0 1 2
var c = fun(0).fun(1); c.fun(2); c.fun(3); //undefined 0 1 1
//直接调用内部函数不会产生闭包
//如果不产生新的闭包,闭包中的数据仍然是原来的数据