闭包一词在javascript中非常有名,对于前端来说理解它至关重要,很多高级应用都要依靠闭包实现。掌握了它,对我们的js写代码水平会有很大帮助,因此成为许多公司前端的面试题之一,用来测试应聘者的js水平,可见其重要性,所以作为一个前端,理解闭包是必须的!没有任何理由说不懂!
理解闭包,首先必须理解变量作用域和作用域链。在JavaScript中,JavaScript有两种作用域:全局作用域和函数作用域。函数内部可以直接读取全局变量。函数子作用域可以访问父作用域的变量 。
var n = 1;function f1() { console.log(n);}f1() // 1复制代码
上面代码中,函数f1可以直接访问全局变量n, 但是在函数外部无法直接读取函数内部的变量,
function f1() { var n = 1;}console.log(n)//Uncaught ReferenceError: n is not defined 提示n没有定义复制代码
如果出于种种原因,需要得到函数内的局部变量。正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。
function f1() { var n = 1; function f2() { console.log(n); // 1 }}复制代码
上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。子对象会顺着作用域链找父对象的变量, 如果想要函数外部访问函数内部的变量,那么可以使用return把f2作为返回值,不就可以在发外部访问到内部的变量了吗?
function f1() { var n = 1; function f2() { console.log(n); // 1 } return f2}var result = f1();result(); // 1复制代码
上面代码中,函数f1的返回值就是函数f2,由于f2可以读取f1的内部变量,所以就可以在外部获得f1的内部变量了。 f2就是闭包,。闭包是指有权访问另一个函数作用域中的变量的函数。在这里要注意的一点:由于通常闭包都是匿名函数,所以给人造成错觉,只有匿名函数才能作为闭包,其实,命名、匿名函数都是可以作为闭包函数的,只不过通常闭包都是作为返回值,自身很少被调用,所以也就没了命名的必要,而命名函数基本上都是要调用的。
另外看了网上一些文章,是通过作用域的提升来解释的, f1是一级作用域,f2是二级作用域,f1返回f2后,把f2的作用域提升到一级作用域,就可以在外部被全局调用了,这种说法通俗易懂,很好理解,但是
闭包的优点
1.函数内部的定义的变量可以保存在内存中。一般函数运行后,函数内部的变量就会被销毁,但是由于闭包的存在,该函数内部的变量就不会被销毁回收,如上述代码中,f1调用后,闭包f2会调用变量n,使得n始终存在内存中。 2.避免全局变量的污染,全局变量是可重用但是污染全局,局部变量不会污染全局但是不可重用。而闭包就是二者优点的结合, 3.是封装对象的私有属性和私有方法。举例如下:
function Person(name) { var _age; function setAge(n) { _age = n; } function getAge() { return _age; } return { name: name, getAge: getAge, setAge: setAge };}var p1 = Person('张三');p1.setAge(25);p1.getAge() // 25该例子来源与阮一峰的博客函数闭包内容,复制代码
上面代码中,函数Person的内部变量_age,通过闭包getAge和setAge,变成了返回对象p1的私有变量。
闭包的缺点
闭包会保留外层函数的内部变量,所以内存消耗很大。因此不能滥用闭包,否则会造成网页的性能问题。
备注:之前闭包在iE浏览器上存在内存溢出的问题,不过这是由于ie的垃圾回收机制引起的,目前已经修复这个问题,