1.闭包概念
闭包(closure):是一个函数在创建时,允许该自身函数访问并操作该自身函数变量之外的变量时所创建的作用域。且值得注意的是:但在函数外部无法直接读取函数内的局部变量。理解闭包的前提是,先理解JavaScript的作用域。
[传送门:作用域与作用域链](https://www.cnblogs.com/syfwhu/p/4839562.html)
var outerValue = 'ninja'; var later;//声明一个空变量稍后再使用。根据命名可以看到 function outerFunction(){ var innerValue ="sam";//在函数内部声明一个值。该变量的作用域是限制在该函数内部,并且在函数外部访问不到 function innerFunction(){//在外部函数内,声明一个内部函数。注意:声明该函数时,innerValue是在作用域内的 assert(outerValue, "I can see the outerValue:" + outerValue); assert(innerValue, "I can see the innerValue:" + innerValue); } later = innerFunction;//将内部函数引用到later变量上。由于later在全局作用域内,所以我们可以对它进行调用 } outerFunction();//调用外部函数,将会声明内部函数,并且将内部函数赋值给later变量 later();//通过later调用内部函数。我们不能直接调用内部函数,因为它的作用域(和innerValue在一起)被限制在outerFunction()内
在外部函数中声明innerFunction()的时候,不仅仅是声明了函数,还创建了一个闭包,该闭包不仅包含函数声明,还包含了函数声明的那个时刻点上该作用域中的所有变量。
这闭包就像保护气泡一样,只要innerFunction()函数一直存在,它的闭包就保护该作用域中即将被垃圾回收的变量。
如果没有闭包,同时做多件事情(产生多个气泡,多个闭包)的时候,无论是事件处理,还是动画,甚至是Ajax请求,都将是极其困难的。如果大家一直想知道关注闭包的理由,那么这就是理由之一!
[jQuery作者 John Resig]
2.使用闭包的注意事项:
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
var name = "mary"; var role = "teacher"; var Person = { name : "jake", role:"person", getName:function(){ return this.name; }, setName:function(newName){ this.name = newName; }, getRole:function(){ return this.role; }, getRoleByThis:function(){//闭包理解的重点对比样例:1 return function(){//返回的函数,发生变化的是被调用的上下文对象的变化 return this.role; } }, getRoleByThat:function(){//闭包理解的重点对比样例:2 var that = this;//保存上一级(即:保存真正想调用的根对象,而不是全局的window)作用域(闭包) return function(){//返回的函数,发生变化的是被调用的上下文对象的变化 return that.role; } }, setRoleByThis:function(){ return function(newRole){ this.role = newRole; } }, setRole:function(newRole){ this.role = newRole; } } var jake = Person;//实例对象:Person;jake console.log("jake.getName():",jake.getName());//成员方法被调用的上下文对象:jake jake.setName("johnny");//成员方法被调用的上下文对象:jake console.log("jake.getName():",jake.getName());//成员方法被调用的上下文对象:jake console.log("*******************************************"); console.log("jake.getRole():",jake.getRole());//成员方法被调用的上下文对象:jake console.log("before execute:jake.setRoleByThis()('senior'):jake.getRole():",jake.getRole()); jake.setRoleByThis()("senior");//成员方法被调用的上下文对象:window console.log("executed:jake.setRoleByThis()('senior'):jake.getRole():",jake.getRole()); console.log("jake.getRole():",jake.getRole()); console.log("jake.getRoleByThis()():",jake.getRoleByThis()());//成员方法被调用的上下文对象:window console.log("jake.getRoleByThat()():",jake.getRoleByThat()());//成员方法被调用的上下文对象:jake console.log("window.role:",window.role);//成员方法被调用的上下文对象:window console.log("*******************************************"); console.log("jake.getRole():",jake.getRole()); console.log("before execute:jake.setRoleByThis()('senior'):jake.getRole():",jake.getRole()); jake.setRole("student");//成员方法被调用的上下文对象:jake console.log("executed:jake.setRoleByThis()('senior'):jake.getRole():",jake.getRole()); console.log("jake.getRole():",jake.getRole()); console.log("jake.getRoleByThis()():",jake.getRoleByThis()());//成员方法被调用的上下文对象:window console.log("jake.getRoleByThat()():",jake.getRoleByThat()());//成员方法被调用的上下文对象:jake console.log("window.role:",window.role); console.log("*******************************************");
3.作用域
4.闭包用处
闭包的最大用处:
①可以读取函数内部的变量;
②让这些变量的值始终保持在内存中。
function f1(){ var n=999; nAdd=function(){ n+=1; } function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000
5.参考文献
[学习Javascript闭包(Closure)](http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html)
[JavaScript忍者秘籍]
[JavaScript之作用域与闭包详解](https://www.cnblogs.com/syfwhu/p/4839562.html)