在javascript中变量有全局作用域和局部作用域。通常,函数内部可以访问全局变量,也可以访问用var声明的局部变量,但是在函数外部不能访问函数内部的局部变量。那么,有时候我们要访问局部变量,如何访问函数内部的局部变量呢?可以用闭包。通俗的讲,闭包就是函数的返回值是一个函数(不是变量),这个返回的函数引用了外部函数的变量。
1 function add() { 2 3 arg = arguments; 4 5 var add_number = function() { 6 7 var sum = 0; 8 9 for (var i = 0; i < arg.length; i++) { 10 11 sum += arg[i]; 12 13 } 14 15 return sum; 16 17 } 18 19 return add_number; 20 21 } 22 23 var temp = add(1, 2, 10)(); 24 25 console.info(temp); //13 26 27 28 29 console.info(arg); //[1, 2, 10]
咦?在函数外面竟然还能访问函数内部的变量arg?这就是闭包的神奇特效。因为add()的返回值是一个函数,再把这个函数赋值给一个全局变量,返回值存储有指向其父函数的地址,所以现在全局变量temp就有指向add函数的变量arg的指针,这个指针被存储在内存中(函数执行结束后没有释放),自然就能够在全局范围内访问局部变量咯。
特别注意:调用函数,返回的函数中如果引用了外部函数的变量,那么就是地址引用,而不是值的引用,如果引用地址指向的变量变化,那么变量跟着变化。见以下:
1 function closureArray(){ 2 3 var func = new Array(); 4 5 for(var i = 0;i < 10;i++){ 6 7 func[i] = function(){ 8 9 return i; 10 11 } 12 13 } 14 15 return func; 16 17 } 18 19 20 21 (closureArray())[5](); //10
为什么得到的结果是10而不是0...9呢,因为闭包内的匿名函数中的arguments对象存储的是外部函数的引用(地址),
问题又来了,如果我们想返回的是[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]呢,很简单,只需要破坏闭包函数对外部函数的引用就可以了:
1 function closureArray(){ 2 var func = new Array(); 3 for(var i = 0;i < 10;i++){ 4 func[i] = (function(temp){ 5 return temp; 6 })(i); //把对外部的引用复制给一个块级作用域的变量,每次块级作用域执行完成之后,就会释放引用,下一次调用又会重新复制,从而改变地址 7 } 8 return func; 9 } 10 11 var arr = closureArray(); 12 console.info(arr); //[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
说到这里,又有疑问了,为什么闭包内部引用外部变量存储的是外部函数的引用(而不是值)呢?
这个就是函数作用域链(scope)的问题了。