闭包详解
什么是闭包
函数即使脱离了它原本所在的词法作用域,也能够访问在词法作用定义的变量。因为函数被return出去了,没有执行一直持有函数的执行期上下文,然后就形成了闭包。闭包 = 执行环境 + 函数
这个函数 a 可以放在别的文件里面,然后得到的 b 函数,不管你在什么时候调用,都会正确打印「hello,闭包」,一般的 a 函数执行完,hello 变量就会被释放,但是在闭包里面,这个 hello 直到 b 函数执行之前,hello 都不会被释放。应用场景太多了,定时器、防抖按钮 什么的都需要用到闭包,下面我们看看应用场景
应用闭包的主要场合:
定时器
大家很容易就想到输出5个5
例一:函数每个一秒返回一个5
for (var i = 0; i < 5; i++) { setTimeout(function () { console.log(i) }, 1000 * i) }
上面可以简写为如下:
for (var i = 0; i < 5; i++) { var temp = function () { console.log(i) } setTimeout(temp , 1000 * i) } temp()
例二:每隔1秒返回一个undefine
for (var i = 0; i < 5; i++) { setTimeout(function (i) { console.log(i) }, 1000 * i) }
上面可以简写为如下:
for (var i = 0; i < 5; i++) { var temp= function (i) { console.log(i); } setTimeout(temp, i * 1000); } temp()
例三:结果是立即输出0,1,2,3,4,
for (var i = 0; i < 5; i++) { setTimeout((function(i) { console.log(i); })(i), i * 1000); }
如何模拟定时器的方式输出:
如何输出01,2,3,4,呢?可以考虑立即执行函数或者let的块级作用于的方式解决
设计私有的方法和变量。(团队合作可以封装代码互不影响)
任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数外部访问这些变量。私有变量包括函数的参数、局部变量和函数内定义的其他函数。
把有权访问私有变量的公有方法称为特权方法(privileged method)。
function closure(){ var age =22; var resetAge=function(){ age =20 } var setAge = function (newAge) { age = newAge } var getAge = function(){ console.log(age) return age } var obj ={ resetAge: resetAge, setAge: setAge, getAge: getAge } return obj } let privateData = closure(); privateData.getAge(); privateData.setAge(29) privateData.getAge(); privateData.resetAge(); privateData.getAge();
简化代码如下:
function closure() { var age = 22; //特权方法 return obj = { resetAge: function () { age = 20 }, setAge: function (newAge) { age = newAge }, getAge: function () { console.log(age) return age } } } let privateData = closure(); privateData.getAge(); privateData.setAge(29) privateData.getAge(); privateData.resetAge(); privateData.getAge();
闭包缺点:内存泄漏
闭包会引用包含函数的整个变量对象,如果闭包的作用域链中保存着一个HTML元素,那么就意味着该元素无法被销毁。所以我们有必要在对这个元素操作完之后主动销毁。
闭包中的this对象
在上面这段代码中,obj.getName()()实际上是在全局作用域中调用了匿名函数,this指向了window。
这里要理解函数名与函数功能是分割开的,不要认为函数在哪里,其内部的this就指向哪里。
window才是匿名函数功能执行的环境。
如果想使this指向外部函数的执行环境,可以这样改写: