共计 3071 个字符,预计需要花费 8 分钟才能阅读完成。
介绍你下你理解的闭包?不管怎样!我最近听到很多次!感觉是不好好总结一下没法面对那些犀利的追问!
如果觉得闭包理解的很透彻,就直接跳到最后看题目!
1. 闭包概念
小红书的解释 闭包 是有权访问另一个函数作用域中的变量的函数。明白了吗?就是一个函数,一个可以访问其他函数中变量的函数。所以常见的创建闭包的方式就是在一个函数内部创建另一个函数。
function bag(num){return function(){return num}
}
var bagc = bag(12)
console.log(bagc()) //12
可以看到在 bag 内部的匿名函数可以访问外部 bag 函数的变量 num。
2. 闭包的用处
闭包可以用在许多地方。它的最大用处有两个,一个是可以 读取函数内部的变量 ,另一个就是让这些 变量的值始终保持在内存中。
function f1(){
var n=999;
nAdd=function(){n+=1}
function f2(){alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd(); // 变量 n 被保存了
result(); // 1000
上面是阮一峰在文档中的一个例子,读取函数内部变量我觉得用处一般吧,让变量保持在内存中的用处倒是不少,像经常使用的 that=this 等。下面看一个常见的问题:
for(var i = 0;i <10;i++){setTimeout(()=>{console.log(i)
},1000)
}
// 上面的代码我们希望按照索引值打印,结果却打印了 10 个 10,为什么就不解释了,i 是全局变量。// 换成下面的写法,就能解决问题,正是因为闭包 让变量的值始终保持在内存中,每个 i 都存在了 num 这个局部变量中
for(var i = 0;i <10;i++){(function(num){setTimeout(()=>{console.log(num)
},1000)
})(i)
}
3. 使用闭包需要注意的点
闭包虽然在解决一些问题上有好处,但是由此引发的一些问题要注意,而且由于闭包会携带外部函数作用域,所以内存占用比较大,所以尽量少用、慎用闭包。
1. 变量问题
正是因为闭包可以使用外部变量,所以下面的代码中,返回的匿名函数中对变量 i 的使用将会是最终的值,数组中存放的函数的返回值将都会是 10。
function test() {var result = [];
for(var i = 0; i<10; i++){result.[i] = function () {return i;}
}
return result
}
需要将上述代码改写成如下:
function test() {var result = [];
for(var i = 0; i<10; i++){result.[i] = function (num) {return function() {console.info(num);
}
}(i)
}
return result
}
此时访问的 num,是上层函数执行环境的 num,数组有 10 个函数对象,每个对象的执行环境下的 number 都不一样。
2.this 问题
匿名函数的执行具有全局性,所以闭包函数的 this 一般指向 window;
var object = {
name: "object",getName:function() {return function() {console.info(this.name)
}
}
}
object.getName()() // underfined
// 因为里面的闭包函数是在 window 作用域下执行的,也就是说,this 指向 windows
可以改写成如下:
var object = {
name: "object",getName:function() {
var that = this;
return function() {console.info(that.name)
}
}
}
object.getName()() // object
3. 内存泄漏问题
如果闭包在作用域链中保存着 html 元素,则该元素内存将无法自动销毁。
function showId() {var el = document.getElementById("app")
el.onclick = function(){aler(el.id) // 这样会导致闭包引用外层的 el,当执行完 showId 后,el 无法释放
}
}
// 改成下面
function showId() {var el = document.getElementById("app")
var id = el.id
el.onclick = function(){aler(id) // 这样会导致闭包引用外层的 el,当执行完 showId 后,el 无法释放
}
el = null // 主动释放 el
}
4. 闭包练习题
好了,看到这里是不是感觉对闭包理解的很到位了?别着急,看看这两个小问题测试一下!
1. 与函数调用结合
var Test = {close:function(val){return function (z){return ++ val +z}
}
}
var getClose = function(val){return Test[val]
}
var fn = getClose('close')
var cover = fn(100)
console.log(cover(200))
console.log(cover(300))
console.log(fn(100)(200))
console.log(fn(100)(200))
console.log(getClose('close')(100)(300))
// 输出结果见结尾处
2. 与 dom 结合
var container1 = document.getElementById('container1')
var container2 = document.getElementById('container2')
var container3 = document.getElementById('container3')
var container4 = document.getElementById('container4')
var container5 = document.getElementById('container5')
var innerHTML = 'window 的 html'
var events = {
innerHTML:'我是 events',
getHtml:function (){console.log(this.innerHTML)
},
setFun:function(){return this.getHtml},
proxy:function(){
var self = this;
return function(){self.getHtml()
}
}
}
container1.onclick = events.getHtml;
container2.onclick = events.setFun();
container3.onclick = events.proxy();
container4.onclick = function(){window.setTimeout(events.setFun(),0)
}
container5.onclick = function(){window.setTimeout(events.proxy(),0)
}
还 ok?有没有被绕晕!晕了就打开电脑敲吧
看一下输出结果吧
第一题:401 402 301 301 401
第二题:container container2 我是 events window 的 html 我是 events
正文完
发表至: javascript
2019-08-07