当一个函数即便在离开了它的词法作用域(Lexical Scope)的情况下,仍然存储并可以存取它的词法作用域(Lexical Scope),这个函数就构成了闭包。由定义我们不难发现闭包和作用域有很多联系。
这就是上面所提到的js作用域问题了,函数内形成自己的作用块级作用域,可以访问全局变量。反之却不行,全局作用域内,却不能访问块级作用域的变量。但如果我们想访问到呢,第一种方式当然就是设置为全局变量,第二种就是用闭包了,下面看第二种解决方案。
function outer() { var local_var = "I'm local"; //不会被立刻回收 return function inner() { //闭包函数 return local_var; }; } var getLocal_var = new outer(); // 当然你也可以直接new outer()(); var local_varValue = getLocal_var(); 最常见setTimeout函数问题 for (var i = 1; i <= 5; i++) { setTimeout( function timer(){ console.log( i ); }, i*1000 ); }这个例子相信大家再熟悉不过了,首先timer()形成闭包函数。直觉上都会认为输出为1,2,3,4,5。很明显这个结论是错误,实际结果为6,6,6,6,6。 首先是js执行为单线程,所以当循环执行完之后才会开始执行setTimeout函数。所以最后执行的时候i已经变成6了,可能有人会问,每一个闭包不应该都会有自己的一份拷贝吗?恭喜你,你的理解是对的,但这个方法却没有这样做,它都是调用了i这个变量,也就是说没有保存自己的那一个i。那么问题也就好解决了,我们给它传一个属于自己i。
for (var i=1; i<=5; i++) { (function(j){ setTimeout( function timer(){ console.log( j ); }, j*1000 ); })(i); } 来看一下阮一峰老师的思考题 var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ var this_ = this; // 把第二个集合在一起了 return function(){ return this.name+'加'+this_.name; }; } }; alert(object.getNameFunc()());相信大家已经知道答案 this. name = The Window ,this_.name = My Object;,首先我们需要解析的是object.getNameFunc()()到底是怎么运行的呢?
首先 object.getNameFunc = function(){ var this_ = this; return function(){ return this.name+'加'+this_.name; }; } 其次 object.getNameFunc() = function(){ return this.name+'加'+this_.name; };而最后的object.getNameFunc()()相信大家已经知道它执行的是哪一个函数了。这边还会涉及到一个this指向的问题。永远指向调用它的函数,如果没有就指向window。所以this_ 指向了object 对象 。而 this 没有指向任何对象,所以指向了window,这也就是答案的由来。
复杂的面试题 function fun(n,o) { // 第一个fun console.log(o) return { fun:function(m){ // 第二个fun return fun(m,n); // 还是第一个 fun } }; } var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,? var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,? var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,?可能很多人知道答案,却不是很理解,仔细看下面的分析,主要我们需要认清fun指向的是哪一个,最终执行的函数是什么
1: fun(0) 最后返回的是什么,作用是什么? a = { fun:function(m){ // 这是返回 return fun(m,n); } } 而作用就是将n置为了0,可能有人会问,执行完不应会被回收吗?当然不是这样,这个n = 0这个参数被下面的函数所引用,所以不会被垃圾回收机制销毁,这就是闭包的作用之一。 a.fun(1) = fun(0).fun(1); a.fun(2) = fun(0).fun(2); …… 实际上就是在执行 fun(1,n) fun(2,n) fun(3,n) 当然是有返回值的,我们这边不讨论,又因为n被赋值为0,所以其实就是在执行 fun(1,0) fun(2,0) fun(3,0) 这样相信大家都知道打印为0 0 0了吧2:fun(0).fun(1)相信大家都知道为0,但是我们还需要关注一下这个结果的返回值是什么? fun(0).fun(1) 最后执行的为fun(1,0) {n = 0,o = 0} 所以返回值依然是 { fun:function(m){ // 这是返回 return fun(m,n); } 所以fun(0).fun(1).fun(2) 就是在执行fun(2,1) 打印1 n = 2 ,o = 1,以此类推 fun(0).fun(1).fun(2).fun(3) 打印23:和第二题一样,fun(0).fun(1) 依次打印 underfined 和0 c.fun(2) = fun(0).fun(1).fun(2) 根据第二题的分析为1,c.fun(3) = fun(0).fun(1).fun(3),这边不在是2而是1,虽然第二次的n已经变成了2,但是因为你又重新执行了fun(0).fun(1) n重新被赋值为1了,所以最后执行的就是fun(3,1) 打印1