首页 > IT职业 > IT图书 > 理解JavaScript的闭包
理解闭包需要搞懂JS的作用域,我另外一篇文章:理解JavaScript的作用域。
function foo() {
var a = 2;
function bar() {
console.log(a);
}
return bar;
}
var baz = foo();
baz(); // 2
我们知道 bar 函数里面的 a 变量在外面是访问不到的,但调用 baz 方法为什么仍然能输出 2 呢?
原因:
foo 函数把 bar 方法函数作为返回值,且赋值了给 baz 变量。baz 变量就存在了对 bar 函数的引用。baz 方法时,a去它的父级作用域(foo函数)查询得到该值,所以就输出了 a 值。这就是闭包的作用之一:把函数作为返回值,外部的作用域可以能访问到函数作用域内部的变量。
function foo() {
var a = 2;
function baz() {
console.log(a); // 2
}
bar(baz);
}
function bar(fn) {
fn();
}
foo()
函数作为参数传递到其他函数中的原理和函数作为返回值差不多:
foo 函数的 bar 方法把内部的 baz 方法传入外部的 bar 方法中。bar 方法调用了传入的baz方法,a去它的父级作用域(foo函数)查询得到值,所以就输出了 a 值。这就是闭包的作用之二:函数作为参数传递到其他函数中,外部函数作用域也可以访问到其他函数内部作用域的变量。
在循环里面添加一个定时器,想预期每秒打印一次对应的值,如按顺序输出1,2,3,4,5
以下代码是不能完成预期结果,都输出 6,6,6,6,6,原因:i 都是同一个引用for循环最后的结果: 6
for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
i 当做参数传入一个立即执行函数中,立即执行函数每次迭代时都创建一个新的作用域。i 拥有了自己的函数内部作用域,保存到每个对应的 i 值 ,就能到达预期的输出 1, 2, 3, 4, 5结果。for (var i=1; i<=5; i++) {
(function(j) {
setTimeout( function timer() {
console.log( j );
}, j*1000 );
})( i );
}
方案1可知,使用ES6的 let 声明也有块级作用域。
for循环头部的let声明,每次迭代都会声明一次 i,那么每个 i 就有了自己的作用域,也能达到预期输出结果。
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
这就是闭包的作用之三:能让一个变量长期存储在内存中。
// 检测一个数字是否被使用过
function isFirstLoad() {
var _list = []
return function (id) {
if(_list.indexOf(id) >= 0) {
return false
}else {
_list.push(id)
return true
}
}
}
// 使用
// 如果第一次使用该数字则返回true,反之返回false
var firstLoad = isFirstLoad()
firstLoad(10) // true
firstLoad(10) // false
firstLoad(20) // true
// 创建10个<a>标签,点击的时候弹出来对应的序号
for (var i = 0; i < 10; i++) {
(function (i) {
var a = document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function (e) {
e.preventDefault()
console.log(i)
})
document.body.appendChild(a)
})(i)
}
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/1747/viewspace-2822851/ ,如需转载,请注明出处,否则将追究法律责任。