轻松理解闭包
2016-02-16 11:45
309 查看
啥是闭包?
一个闭包就是一个可以访问外部函数作用域链的一个内部函数。闭包有三个作用域链:它可以访问自己的作用域(即当前的大括号内),它也可以访问外部函数的变量,它还可以访问全局变量。闭包不仅可以访问外部函数的变量,还可以访问外部函数的参数。注意:尽管内部函数可以直接访问外部函数的参数,但并不能调用外部函数的arguments 对象。
我们可以通过在一个function里增加一个function来创建一个闭包。
一个简单的闭包示例 in JavaScript:
function showName (firstName, lastName) { var nameIntro = "Your name is "; // this inner function has access to the outer function's variables, including the parameter function makeFullName () { return nameIntro + firstName + " " + lastName; } return makeFullName (); } showName ("Michael", "Jackson"); // Your name is Michael Jackson
闭包在node.js中广泛使用,它是node.js的异步和无阻塞的工具。在jQuery以及几乎每一段js代码中,闭包都被广泛使用。
jQuery中一段经典的闭包代码:
$(function() { var selections = []; $(".niners").click(function() { // this closure has access to the selections variable selections.push (this.prop("name")); // update the selections variable in the outer function's scope }); });
闭包的特性及副作用
1. 外部函数返回后,闭包依然可以访问外部函数中的变量
这是闭包中重要的特性之一。是的,你没看错。在js函数执行过程中,内部函数与外部函数使用相同的作用域链。这意味着即使外部函数返回之后,内部函数依然可以访问其中的变量。所以我们可以稍后在程序中调用内部函数。示例代码:
function celebrityName (firstName) { var nameIntro = "This celebrity is "; // this inner function has access to the outer function's variables, including the parameter function lastName (theLastName) { return nameIntro + firstName + " " + theLastName; } return lastName; } var mjName = celebrityName ("Michael"); // At this juncture, the celebrityName outer function has returned. // The closure (lastName) is called here after the outer function has returned above // Yet, the closure still has access to the outer function's variables and parameter mjName ("Jackson"); // This celebrity is Michael Jackson
2. 闭包保存了对外部函数中的变量的引用
并不是保存实际的变量值。 当闭包被调用之前,如果外部函数的变量发生变化时,闭包会很有意思。闭包的这种特性有很多用途。例如下面这个私有变量的例子(first demonstrated by Douglas Crockford):function celebrityID () { var celebrityID = 999; // We are returning an object with some inner functions // All the inner functions have access to the outer function's variables return { getID: function () { // This inner function will return the UPDATED celebrityID variable // It will return the current value of celebrityID, even after the changeTheID function changes it return celebrityID; }, setID: function (theNewID) { // This inner function will change the outer function's variable anytime celebrityID = theNewID; } } } var mjID = celebrityID (); // At this juncture, the celebrityID outer function has returned. mjID.getID(); // 999 mjID.setID(567); // Changes the outer function's variable mjID.getID(); // 567: It returns the updated celebrityId variable
3. 闭包很乱
因为闭包可以访问外部函数的变量,所以在for循环中当外部函数的变量发生变化时容易出bug。// This example is explained in detail below (just after this code box). function celebrityIDCreator (theCelebrities) { var i; var uniqueID = 100; for (i = 0; i < theCelebrities.length; i++) { theCelebrities[i]["id"] = function () { return uniqueID + i; } } return theCelebrities; } var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}]; var createIdForActionCelebs = celebrityIDCreator (actionCelebs); var stalloneID = createIdForActionCelebs [0]; console.log(stalloneID.id()); // 103
在前面的例子中,匿名函数被调用时i的值是3。所以每个返回的array 都会得到 i=103,而不是 100, 101, 102。原因是闭包访问外部函数的变量,是通过引用,并不是直接访问值。前面的例子展示了我们可以通过闭包访问更新的变量,这个例子也是在i变化时访问它,因为外部函数运行整个循环返回的最后的i值是103。
为了修复这个bug,可以使用一个立即执行函数表达式 Immediately Invoked Function Expression (IIFE), 代码如下:
function celebrityIDCreator (theCelebrities) { var i; var uniqueID = 100; for (i = 0; i < theCelebrities.length; i++) { theCelebrities[i]["id"] = function (j) { // the j parametric variable is the i passed in on invocation of this IIFE return function () { return uniqueID + j; // each iteration of the for loop passes the current value of i into this IIFE and it saves the correct value to the array } () // BY adding () at the end of this function, we are executing it immediately and returning just the value of uniqueID + j, instead of returning a function. } (i); // immediately invoke the function passing the i variable as a parameter } return theCelebrities; } var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}]; var createIdForActionCelebs = celebrityIDCreator (actionCelebs); var stalloneID = createIdForActionCelebs[0]; console.log(stalloneID.id); // 100 var cruiseID = createIdForActionCelebs[1]; console.log(cruiseID.id); // 101
相关文章推荐
- ssh学习:hibernate关系映射(1)
- ssh学习:hibernate关系映射(2)
- ssh学习:hibernate关系映射(3)
- ssh学习:hibernate关系映射(4)
- ssh学习:hibernate关系映射(5)
- ssh学习:hibernate中QBC查询
- ssh学习:hibernate中Native SQL查询
- 自定义泛型类获取子类的实体类类型
- ssh学习:hibernate中组件属性
- ssh学习:Spring注入方式
- ssh学习:Spring中Bean的生命周期
- 分布式版本控制工具 Git
- magento < 1.9 xss 漏洞修复说明
- 装饰模式
- Ubuntu 14.04进不了登录界面的修复
- 从2月14号开始,上传AppStore会碰到:Failed to locate or generate matching signing assets
- ssh学习:struts2的获取web资源模式
- ssh学习:struts2的拦截器实现
- ssh学习:struts2的动态调方法调用
- ssh学习:struts2的返回结果集input