JavaScript——以简单的方式理解闭包
闭包,在一开始接触JavaScript的时候就听说过。首先明确一点,它理解起来确实不复杂,而且它也非常好用。那我们去理解闭包之前,要有什么基础呢?我个人认为最重要的便是作用域(lexical scope),如果对作用域和作用域链不理解的同学最好自己先去学一学,再回过头来,理解闭包,就更加轻松。
下面便直接进入主题。
我们知道一个函数是有作用域的,在函数内部定义的局部变量只有在函数内部才可以访问的到。一旦函数访问结束被销毁,局部变量随之也会销毁,无法通过任何方式再次访问局部变量,除了闭包。也就是说,我们可以通过闭包这一个方法,从函数的外部去访问到函数内部的变量,即使那个函数已经被销毁。没错,闭包最重要的用法就是,我们只提供某些接口去访问和修改局部变量,而外部是不能直接访问到局部变量的。
说了那么多有关如何使用闭包,我们来看看闭包是长什么样子的。又到了举个栗子的环节,依然是最简单的people和name。
var people = function(){ var name = "Yika"; var sayName = function(){ return name; //访问了people函数的局部变量name } var setName = function(newName){ name = newName; //访问了people函数的局部变量name } return{ sayName: sayName, setName: setName }//返回一个对象 } var p1 = people(); //函数return的是一个对象,这个对象里有两个函数sayName和setName console.log(p1.name); //undefined. name是people函数里的局部变量,而不是p1对象的属性,当然为undefined console.log(p1.sayName());//"Yika" p1.setName("Ruyi"); //通过setName函数修改局部变量name的值 console.log(p1.sayName());//"Ruyi"
看完这个例子,想必对闭包多少有个了解啦,除了注释的内容,下面再做些补充。
问:为什么局部变量name属性在people执行完之后,没有被销毁呢,反而数值还保存在内存中。
答: 在例子中,函数注释那里专门写了(访问了people函数的局部变量name)。正是因为people函数里的sayName函数和setName函数访问了name属性,并且通过return传到了p1对象里,成了p1的两个方法。因为方法一直引用着people函数的局部变量,所以不会被消除,依然会在内存中。这样便形成了闭包,可以在函数外部访问到函数内部的局部变量。
对此,我们可以换个更直观的写法。
var people = function(){ var name = "Yika"; var obj = { sayName: function(){ return name; }, setName: function(newName){ name = newName } }; return obj; //直观的返回对象 } //下面的结果是一样的。
当然闭包也不是一直那么好用,特别是在循环里。继续举例子
<body> <input type="button"/> <input type="button"/> <input type="button"/> <input type="button"/> <input type="button"/> <input type="button"/> <input type="button"/> <script type="text/javascript"> var oBtn = document.getElementsByTagName('input'); for(var i = 0, len = oBtn.length; i < len; i++){ oBtn[i].onclick = function(){ alert("value = " + i); //闭包陷阱的发生地!永远输出 value = 7 } } </script> </body>
当我们运行上面的代码的时候,我们是这样想的,循环一下按钮,并输出按钮所在的序号,但是我们得到的却永远是7。其实用我们之前讲的闭包会让变量的值一直保存在内存中的原理想一想,就应该懂了。当我们循环的时候按钮的点击事件时,是引用了for循环里的 i 变量。当所有按钮都绑定了点击事件后,i 的值也已经变成了7,当然所有的按钮输出的都是7啦!怎么解决这个问题也很好办的,但是我希望留下给大家思考。这里就说一下大致思路吧,我们可以在循环之外创建一个辅助函数,并让辅助函数return一个绑定当前 i 的函数。
当然这里我也只是抛砖引玉的介绍了一下闭包,希望可以帮到大家浅显简单的了解闭包。
还是那句话噢,有问题立即指正,我一定会立马检查更正,以免误导了大家!