闭包与高阶函数

闭包是一个什么概念,之前有人会说一个函数返回一个函数,这就是闭包,真的只有返回函数才叫闭包么?这只是面子上的东西,只看到形式上的东西,并没有抓住本质。

高阶函数是一个和闭包密不可分的的概念,之后你慢慢会发现。

闭包

说到闭包,离不开两个概念,变量作用域和变量生命周期。

1. 变量作用域

变量在其定义的时候,作用域的范围也就确定了。全局变量在任何位置都可以访问。局部变量只能函数内部访问。在函数中访问变量,如果内部找不到,会依次沿着作用域链从内到外搜索。

2. 变量生命周期

全局变量的生命周期是永久的,局部变量会在函数执行完之后被销毁。闭包的作用就是延续了局部变量的生命周期

很经典的一个例子:

1
2
3
4
5
6
7
8
9
var buttons = document.getElementsByTagName("button");
for (var i = 0, len = buttons.length; i < len; i++) {
buttons[i].onclick = clickEvent(i);
}
function clickEvent(i){
return function () {
console.log(i);
}
}

利用闭包的特点,经常用了实现全局变量私有化变量生命周期延续

  • 全局变量私有化

    1
    2
    3
    4
    5
    6
    var sum = (function() {
    var result = 0;
    return function(a) {
    return result += a;
    }
    })()
  • 变量生命周期延续

    1
    2
    3
    4
    5
    6
    7
    8
    var report = (function() {
    var images = [];
    return function(src) {
    var img = new Image()
    images.push(img)
    img.src = src;
    }
    })()

像上面例子中的变量result和images,对于他们内部返回的函数来说,他们就是自由变量,既不是参数又不是内部变量,而且他的生命周期与内部函数一致,即使创建环境消失也不例外,这种形式就叫做闭包。

高阶函数

至少满足以下一个条件的函数就可以成为高阶函数。

  1. 函数可以作为参数传递。

  2. 函数可以作为返回值输出。

这么看来,闭包是不是和高阶函数密不可分呢。

1. 函数作为参数

运用的方面很多,最典型的情况就是回调函数。实际上我们就是将可变的逻辑代码部分从不变的部分抽离出来,这样我们的代码就可以非常灵活,满足不同的需求。比方说Array.prototype.sort,我们通过回调函数将我们的排序具体实现告诉数组,分分钟钟实现多种排序效果。

2. 函数作为返回值输出

让函数返回一个可执行的函数,那这个操作就可以延续下去,常常用在函数包装上面。

下面是一个包装resize函数的方法,由于resize触发很频繁,因此通过setTimeout进行节流控制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var resize = function(fn, interval) {
var self = fn,
timer,
firstTime = true;
return function() {
var args = argumensts,
that = this;
if(firstTime) {
self.apply(this, argumensts);
return firstTime = false;
}
if(timer) {
return false;
}
timer = setTimeout(function() {
clearTimeout(timer);
self.apply(that, args);
timer = null
}, interval || 500)
};
}
window.onresize = resize(function() {
console.log("resize")
})

总结

闭包和高阶函数是设计模式的基础,通过闭合和高阶函数的组合实现多种设计模式,之后遇到再做介绍。