闭包:
-
首先讲讲闭包的定义:
-
在我看来,闭包有三大特点。一是函数嵌套函数,二是外部函数可以访问内部函数的变量,三是参数和变量不会被垃圾回收机制回收。
-
所以简单来说我认为闭包可以理解为是连接函数内部和函数外部的一座桥梁。
-
-
然后讲讲闭包的原理:
- 为什么会形成闭包呢,因为当内部函数被返回至外部并保存时,一定会形成闭包。
- 而关于闭包为什么能访问内部函数的变量,且不会垃圾回收机制回收,是因为闭包会产生原来的作用域链不被释放。
- 我们从JS预编译的角度来讲,可以知道当外部函数执行的前一刻,内部函数被定义,此时他的作用域链和外部函数的作用域链完全相同,都会指向该外部函数的AO(函数上下文)。然后等到外部函数执行结束,原本应被释放的外部函数的AO却因为内部函数被返回至外部且有一个变量进行接收保存,因此该外部函数的AO没有被销毁,还被内部函数的作用域链连接着。所以当我们进行访问内部函数的变量时,若该内部函数查询不到该变量,则它会向外部函数中的AO去寻找,若无则沿着其自身作用域链依次往上级查找。
-
闭包的优缺点:
-
闭包的优点:
- 可以将一个变量长期存储在内存中,用于缓存
- 可以避免全局变量的污染
- 加强封装性,实现了对变量的隐藏和封装
-
闭包的缺点:
- 因为函数执行上下文AO执行完后不被释放,所以会导致内存消耗很大,增加了内存消耗量,影响网页性能出现问题
- 而且过度的使用闭包可能会导致内存泄露,或程序加载运行过慢卡顿等问题出现。
-
所以我们可以在退出函数之前,将不使用的局部变量进行删除。
-
-
然后讲讲闭包的应用场景:
- 定时器setTimeout
对于原生的setTimeout来说,我们很难对第一个函数进行传参
setTimeout(function(a) { console.log(a) }, 1000)
我们可以使用闭包,在函数外部对内部函数进行传参以达到目的
function func(param) { return function() { console.log(param) } } var f1 = func(66) setTimeout(f1,1000)
- 私有化变量,封装功能集
比如说我们有个应用场景,是对某个属性的值进行增删减查,例如拿一个简单的学生管理系统来说,
我们可以对外部函数的变量students进行私有化,在外部访问内部函数的方法改变私有变量students的值
function myClass() { var students = [] var operations = { joinStu: function(name) { students.push(name) }, leaveStu: function(name) { for(var i=0; i<students.length; i++) { var item = students[i] if(item === name) { students.splice(i, 1) } } }, findStu: function() { console.log(students) } } return operations } var class1 = myClass() class1.findStu() // [] class1.joinStu('Trist') class1.findStu() // ["Trist"] class1.joinStu('张三') class1.findStu() // ["Trist", "张三"] class1.leaveStu("张三") class1.findStu() // ["Trist"]