闭包简介
JavaScript是一门完整的OOP语言,但同时也有许多函数式语言的特性
在es6模块化出来之前,变量的作用于和生存周期,基本和闭包的形成有关
变量的作用域及生命周期
变量的作用域
function func(){
var a = 1;
console.info(a);
}
func(); //1
console.info(a); //Uncaught ReferenceError: a is not defined(…)
变量的生命周期
function func1(){
var a = 1;
console.info(a);
}
func2(); //执行完退出函数后局部变量a被销毁
function func2(){
let a = 1;
return function(){
a++;
console.info(a);
}
}
let do = func2();
do(); //2
do(); //3
do(); //4
func1中执行完退出函数后局部变量a被销毁
func2中a的值被保留下来
经典事例
我们现在想在页面中添加三个div,分别点击的时候打印出该div的索引值。
<!doctype html>
<html>
<body></body>
<script type = 'text/javascript'>
'use strict';
var arr = [0, 1, 2];
var div;
for(var i = 0 ; i < arr.length ; i++){
div = document.createElement('div');
div.innerHTML = i;
div.onclick = function(){ console.info(i) };
document.body.appendChild(div)
}
</script>
</html>
上面这段代码看上去行云流水,创建标签,赋值并添加onclick方法。但是在点击每一个div的时候,打印结果都是:3!
这是因为:
点击事件为异步触发时,循环已结束,所以无论点击哪个div,输出结果都是固定的(3)
解决方法1:
<!doctype html>
<html>
<body></body>
<script type = 'text/javascript'>
'use strict';
var arr = [0, 1, 2];
var div;
//将循环定义变量的var修改为let,es6之后块级作用域let,因为不是主讲内容,有兴趣的朋友可以去了解一下为什么
for(let i = 0 ; i < arr.length ; i++){
div = document.createElement('div');
div.innerHTML = i;
div.onclick = function(){ console.info(i) };
document.body.appendChild(div)
}
</script>
</html>
解决方法2:
<!doctype html>
<html>
<body></body>
<script type = 'text/javascript'>
'use strict';
var arr = [0, 1, 2];
var div;
//在定义div的onclick方法时,使用闭包来处理
//在闭包的帮助下,把每次循环的i都封闭起来,当时间函数顺着作用域链中从内到外查找变量i时,会优先找到封闭在闭包中的i
for(var i = 0 ; i < arr.length ; i++){
div = document.createElement('div');
div.innerHTML = i;
div.onclick = (function(i){
return function(){ console.info(i) }
})(i)
document.body.appendChild(div)
}
</script>
</html>
闭包高阶函数应用
//函数柯里化(currying)
//计算每月的开销
let moneyCost = 0;
let cost = function(money){
moneyCost += money;
}
cost(100);
cost(200);
cost(300);
console.info(moneyCost); //600
//柯里化重写 每天结束都计算,但我们关心月底会花掉多少钱,就是说在月底计算一次就行
let cost = (function(){
let args = [];
return function(){
if(arguments.length === 0){
let money = 0;
for(let i = 0 ; i < args.length ; i++){
money += args[i];
}
//args = []; //如果计算完成需要清空重新计算则打开此注释
return money;
}else{
[].push.apply(args,arguments)
}
}
})();
cost(100); //未真正求值
cost(200); //未真正求值
cost(300, 100); //未真正求值
cost(); //700
习题环节(激动人心的做题时间)
分别打印出什么(答案自己试就行)
例1.
for (var i = 0; i < 5; i++) {
console.log(i);
}
for (let i = 0; i < 5; i++) {
console.log(i);
}
例2.
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
例3.
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000 * i);
}
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000 * i);
}
例4.
for(var i = 0 ; i < 5 ; i++){
setTimeout(function(i){
console.info(i)
}, i * 1000, i)
}
for(let i = 0 ; i < 5 ; i++){
setTimeout(function(i){
console.info(i)
}, i * 1000, i)
}
例5.
for (var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, i * 1000);
})(i);
}
for (let i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, i * 1000);
})(i);
}
例6.
for (var i = 0; i < 5; i++) {
(function() {
setTimeout(function() {
console.log(i);
}, i * 1000);
})(i);
}
for (let i = 0; i < 5; i++) {
(function() {
setTimeout(function() {
console.log(i);
}, i * 1000);
})(i);
}
例7.
for (var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, i * 1000);
})();
}
for (let i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, i * 1000);
})();
}
文章资源参考:《JavaScript设计模式与开发实践》 曾探