什么是闭包
JavaScript
函数是将要执行的代码及执行这些代码的作用域构成的一个综合体。计算机术语称这种代码和作用域的综合体为闭包。故所有JavaScript
函数都是闭包。
但我们常说的JavaScript
闭包是指,一个嵌套函数被导出到它所定义的作用域外时,才明确地称为闭包。
JavaScript闭包
闭包是 JavaScript
一个非常重要的特性,这意味着当前作用域总是能够访问外部作用域中的变量。 因为函数是 JavaScript
中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。
简单写法
该函数的私有持久变量,可以被多个函数共享
1 var uniqueID = (function() { 2 // 私有持久值 3 var id = 0; 4 return function() { 5 return id++; 6 } 7 })();
循环中的闭包
一个常见的错误出现在循环中使用闭包,开发人员在循环语句里创建函数(内部计数)时经常得不到预期的结果,假设我们需要在每次循环中调用循环序号
1 for(var i = 0; i < 10; i++) { 2 setTimeout(function() { 3 console.log(i); 4 }, 1000); 5 }
所输入的内容不是 0-9
,取而代之的是打印10次 10
。 关键原因,在调用console.log(i)
时,循环已经结束,同一个上下文中创建的闭包是共用一个[[Scope]]属性,导致i已经被修改成了10
。 在ECMAScript中,同一个父上下文中创建的闭包是共用一个[[Scope]]
属性的。也就是说,某个闭包对其中[[Scope]]的变量做修改会影响到其他闭包对其变量的读取。
解决方式
避免引用错误,获取正确序号。我们需要引入自执行函数,包裹一下。传入就是i
的拷贝,这样就能获取正确的输出。
1 for(var i = 0; i < 10; i++) { 2 (function(e) { 3 setTimeout(function() { 4 console.log(e); 5 }, 1000); 6 })(i); 7 }
将setTimeout
包裹在一个匿名函数中,匿名函数拥有变量e
的引用,便不用被循环改变了。
使用闭包的断点
在此贴上《JavaScript权威指南》中,使用闭包的断点
代码。由Steve Yen
所写,用来捕获一个函数中的当前作用域(包括局部变量和函数的参数),并返回其结果。
1 function inspect(inspector, title) { 2 var expression, result; 3 if("ignore" in arguments.callee) { 4 return; 5 } 6 7 while(true) { 8 var message = ""; 9 if(title) { 10 message = title + " "; 11 } 12 13 if(expression) { 14 message += " " + expression + " ==> " + result + " "; 15 } else { 16 expression = ""; 17 } 18 19 expression += "Enter an expression to evaluate:"; 20 expression = prompt(message, expression); 21 22 if(!expression) { 23 return; 24 } 25 26 result = inspector(expression); 27 } 28 }
用断点技术计算阶乘的函数
1 function factorial(n) { 2 var inspector = function($) { 3 return eval($); 4 } 5 6 // inspect.ignore = true; 7 inspect(inspector, "Entering factorial()"); 8 9 var result = 1; 10 while(n > 1) { 11 result *= n; 12 n--; 13 inspect(inspector, "factorial() loop"); 14 } 15 16 inspect(inspector, "Exiting factorial()"); 17 return result; 18 }
参考