定义
当函数可以记住并访问它定义时候的作用域,就产生了闭包。也就是说,一个函数在另外一个函数里面定义,但是它执行的时候,可能是在其他的作用域,但是它还是能够凭着出生时的回忆,找到定义时的作用域,去访问里面的变量。
function foo() {
var a = 2;
function bar() {
console.log(a)
}
return bar;
}
var bar = foo();
bar();//记住了定义时的作用域,所以还是可以访问到a,这里会输出2
为什么能够访问到定义时候的作用域呢?答案只有一个,定义闭包的函数是个重情义的家伙,考虑到那个宝宝可能随时要用到它里面的变量,都不肯被回收。没错,这样会导致foo的作用域一直没有被回收,有一点浪费内存空间。
关于this对象
一般函数的this指向的是调用它的对象。由于闭包函数是由window调用的,所以一般它的this指向window。如果你需要让它指向某个对象,可以用call或者把外部的this保存起来,在里面直接使用。
var name = 'window';
var object = {
name: 'My Object',
//var that = this;
getNameFunc: function() {
return function() {
console.log(this.name);
//console.log(that.name);
}
}
}
object.getNameFunc()();//输出'window'
object.getNameFunc().call(object);//输出'My Object'
循环和闭包
很常见的一段代码,运行结果就是输出5个5。因为这里的i没有块级作用域,也就是说闭包里面的函数访问到外部的变量i只有一个,加上setTimeout是异步的,执行到里面的回调的时候,外面的这个i的值就是循环结束后的值5。
for(var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000)
}
在没有let的时候,我们都是通过模仿块级作用域来解决。这个时候,闭包函数定义的作用域变成了(function(){}),然后在这个作用域里面把i的值赋值给j,每个闭包函数访问到的j就是它定义时候作用域已经保存好了的,自然就能够正确的输出。
for(var i = 0; i < 5; i++) {
(function() {
var j = i;
setTimeout(function() {//闭包
console.log(j);
}, 1000)
})()
}
当然,我们一般都舍不得再用一个j来保存,直接用传参数的形式,让块级作用域可以访问到i的值。
for(var i = 0; i < 5; i++) {
(function(j) {
setTimeout(function() {//闭包
console.log(j);
}, 1000)
})(i)
}
简单改一下,每次用个块级作用域的let来保存一下是不是就可以了啊
for(var i = 0; i < 5; i++) {
let j = i;
setTimeout(function() {
console.log(j);
}, 1000)
}
哈哈哈,真的就输出0 1 2 3 4 5了。因为闭包记住了它定义时候的作用域呀,这个时候的j是块级的,能够好好地保存起来被闭包函数使用。
其实就是相当于每次循环都会去声明一个j,然后这个j的作用域就属于这次循环,所以可以直接把j的赋值写在for循环里。
for(let j = 0; i < 5; i++) {
setTimeout(function() {
console.log(j);
}, 1000)
}
模块化
可以使用闭包来实现模块化的。
var module = (function(window) {
function test1() {
}
functon test2() {
}
return {
test1: test1,
test2: test2
}
})(window)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。