理解JavaScript的闭包
栏目: JavaScript · 发布时间: 5年前
内容简介:理解闭包需要搞懂JS的作用域,我们知道原因:
- 闭包真言
- 理解闭包情景一:函数作为返回值
- 理解闭包情景二:函数作为参数传递到其他函数中
- 理解闭包情景三:循环和闭包
- 实际开发中闭包的应用
- 闭包优缺点
闭包真言
- 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); } 复制代码
改进方案1
-
把
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 ); } 复制代码
改进方案2
方案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个标签,点击的时候弹出来对应的序号
// 创建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) } 复制代码
以上所述就是小编给大家介绍的《理解JavaScript的闭包》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 理解 JavaScript 闭包
- 理解 JavaScript 中的闭包
- 简单理解 Scala 的闭包
- [译] 理解 Rust 中的闭包
- [译]理解JS中的闭包
- [译] 我从没理解过 JavaScript 闭包
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。