深入学习 JavaScript —— 闭包

栏目: JavaScript · 发布时间: 4年前

内容简介:作者虽然不是第一次写学习笔记了,但是系统性的专栏确实是第一回写,所以对这篇文章难度的预估量不够,酝酿了蛮久时间的。写之前一直在纠结一个问题,这类问题网上已经有各类大神做出各种很详细的分解了,还有必要写吗? 能让我有动力写的原因,一个是强烈的表达欲望,另一个是希望后来者能通过我这篇文章学到一点东西。基于这两点,我这篇文章的思路就有了:要写得尽可能详细甚至啰嗦,多用一些生活化的例子作比。当然,我这个行文思路也是参考借鉴了【张鑫旭】大神的。好了,接下来开启我的第一篇文章吧——关于 JavaScript 的闭包 (

作者虽然不是第一次写学习笔记了,但是系统性的专栏确实是第一回写,所以对这篇文章难度的预估量不够,酝酿了蛮久时间的。写之前一直在纠结一个问题,这类问题网上已经有各类大神做出各种很详细的分解了,还有必要写吗? 能让我有动力写的原因,一个是强烈的表达欲望,另一个是希望后来者能通过我这篇文章学到一点东西。基于这两点,我这篇文章的思路就有了:要写得尽可能详细甚至啰嗦,多用一些生活化的例子作比。当然,我这个行文思路也是参考借鉴了【张鑫旭】大神的。好了,接下来开启我的第一篇文章吧——关于 JavaScript 的闭包 (closures)。

正文前再啰嗦点什么

我希望看到这篇文章的人能够秉持一个思维习惯:和物理、数学等客观存在的知识点不一样的是,编程领域的知识点有相当大的部分具有主观性——意思就是,很多编程领域的知识首先是人根据现实的需求创造出来的,它不是客观就存在的。这就导致编程领域的一个现象:对于一种问题的解法,我们可以有很多不同角度的思路,这其中前端领域尤为突出。但它们都是人类所创造出来的概念。 这个思维会有助于我们去学习编程领域的知识点(至少我是这样)。回到我们这篇文章的主题,闭包,这个令许多 JS 新手头疼不已的问题,其实只是人们解决函数式编程问题用到的一个概念工具。什么是函数式编程?这个概念比闭包还难说清楚,计划专研后再后续写些文章详细总结。 现在我们就简单地把函数式编程与面向对象、面向过程编程视为同一层次的概念,它是一种编程的思维模式。而闭包只是实现函数式编程的高效 工具 之一。啰嗦了那么多,来看张明白直了的图吧~

深入学习 JavaScript —— 闭包

闭包的定义

上面说到闭包是实现函数式编程的工具之一。但是,闭包到底是什么,长什么样?《你不知道的 JavaScript》书中,将闭包定义为【函数在所定义的作用域之外的区域被调用】。听上去有点绕,我举个比较现实的例子:

闭包的函数定义与使用作用域分离,有点类似古代对军队的掌控和调度权力分离。在大多数情况下,古代军队是只忠于皇廷的;一旦恰逢大战,皇帝不好御驾亲征,这时会将军队的调度权临时给予将领。将领真正掌控了军队吗?正常情况下是没有的,他只有在沙场上调度军队的权力。我们可以看到,军队明面的掌管者是谁?皇帝。但实际的操纵者是谁?将领。 这种权力分离的过程是如何实现的呢?你可能知道的,虎符!虎符是实现这种权力传递,或者分离的概念性工具。

从某种意义上看,闭包和虎符一样,也是一种概念性工具。现在是不是能够勉强记住上面那句,【函数在所定义的作用域之外的区域被调用】?如果可以的话,现在举两个例子,请你判断是否为闭包,以及是否输出正常。最好思考一分钟,然后再看解释。

// example 1
function outter () {
    console.log("outter");
    return function inner () {
        console.log("inner");
    };
}
var p = outter();
p();
复制代码
// example 2
function outter () {
    console.log("outter");
    function inner () {
        console.log("inner");
    }
    foo(inner);
}
function foo (fn) {
    fn();
}
outter();
复制代码

上面两个例子,你有什么看法?事实上,这两个都是《你不知道的 JavaScript》里提及的闭包例子。 第一个例子, outterinner 作为返回值传递给 pp 在全局作用域执行时, inner 的定义所在作用域与执行作用域不同,符合上面提到的闭包定义。 第二个例子, inner 作为参数被传递给 foo 执行,同样的我们可以看到, inner 的定义所在域与执行域不同,虽然它们都是 outter 函数作用域的子集。 不知看到此处的你,对闭包的理解是否清晰些了?如果你之前看过其它博客文章,它们对闭包的定义可能与本文不一致。比如说,在阮一峰这篇博文里,对闭包的定义是【能够读取其它函数内部变量的函数】。可能网上还有其它的定义。事实上,这些定义本质上都是一样的,它们只不过从某个特定角度描述闭包的特性。就像虎符,虽然在各个朝代都有出现,但是它在每个时期的形状是不尽相同的。挑一个你认为最好记的,加上辅助理解的实例就好。

闭包的作用

实际上,我更愿意将阮一峰对闭包的定义——【能够读取其它函数内部变量的函数】,视为闭包的作用之一。但是,它的作用远不止这个。 回到之前提及的函数式编程,它的一大特征就是【函数是一等公民】。这句话怎么理解?大概地说,函数式编程提倡用函数来实现,传统面向对象中只有类和对象才能实现的功能。之前提到,闭包是实现函数式编程的工具之一,其实用处主要就在这里。 闭包如何实现函数式编程呢?其实很简单,既然闭包可于读取函数的内部变量,换个角度想就是, 闭包可以实现函数变量或方法的公有化 。有些同学可能知道,传统面向对象中,只有对象才有私有和共有变量、方法之分,函数内部不仅不能定义函数,也不能主动暴露内部变量。JS 中函数内部是可以定义函数的,相当于有了内部方法。有了闭包,JS 中的函数就和对象很像啦,这就很符合函数式编程的理念了~ 然而,说了那么多干巴巴的定义,对于函数如何当对象使用,你可能还是很懵。没关系,举几个实例来辅助说明。

// example 3
function people (_name, _age) {
    var name = _name;
    var age = _age;
    return function get () {
        console.log('name: ' + name);
        console.log('age: ' + age);
    };
}
var get_info = people("Lee", 20);
get_info();   // name: Lee; age: 20
复制代码

上述例子在一定程度上体现了函数被当作对象的特点。然而,有人可能会钻一个牛角尖:我已经知道它返回的是一个函数了,你只不过是在执行这个函数而已。首先恭喜你,能提出这个疑问,代表你至少看懂上一节内容了;其次,这个示例的重点不在函数的执行,而在这个函数输出了不属于它的变量数据。这在某种程度上,上文也有提到, 实现函数变量或方法的公有化 。 对 JS 有点了解的可能会问:JS 中不是有垃圾回收机制吗,为什么 people 执行后它的内部变量没有被回收?这个问题问得相当好,有一段时间我也很疑惑这点。下面再举一个很经典的计数器例子,我们来看看为什么会这样。

// example 4
function counter () {
    var count = 0;
    console.log("init: " + count++);
    return function increase () {
        count += 2;
        console.log("increase: " + count);
    }
}
var increase1 = counter();    // init: 0
var increase2 = counter();    // init: 0
increase1();    // increase: 3
increase1();    // increase: 5
increase2();    // increase: 3
复制代码

这个例子是计数器的变形,之所以在 counter 内部域中做一个输出,是为了观察执行 increase 时是否会执行 counter 内部的语句。 可以看到,其一, increase 执行时只用到 counter 的变量,并不执行某些特定的语句。但假如引用了另一个函数m,肯定会执行m内部的语句。其二,在代码的运行周期内, count 变量一直保留;只要愿意,可以执行若干次 increase 。这不符合 JS 垃圾回收的机制。其三,两个变量 (increase1&increase2) 引用的计时器变量 count 不同。在 JS 语境下,它们其实是两个对象。 上述疑问中,一大家想一下就能明白了,三不是本文的重心,以后会再提到,重点是疑问二。这里其实涉及到 JS 的函数传递和词法作用域。在 JS 中,【函数是一等公民】还体现在:函数可以作为参数、返回值等进行传递。上述示例中, increase1 获取了 counter 返回的函数 increase ,此时这个函数保留有原父函数 counter 内部变量的引用。为了代码引用非空,JS 对此会进行特殊处理,【保留原父函数的词法域】,但不会再执行。


以上所述就是小编给大家介绍的《深入学习 JavaScript —— 闭包》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

从Paxos到Zookeeper

从Paxos到Zookeeper

倪超 / 电子工业出版社 / 2015-2-1 / 75.00元

《Paxos到Zookeeper:分布式一致性原理与实践》从分布式一致性的理论出发,向读者简要介绍几种典型的分布式一致性协议,以及解决分布式一致性问题的思路,其中重点讲解了Paxos和ZAB协议。同时,本书深入介绍了分布式一致性问题的工业解决方案——ZooKeeper,并着重向读者展示这一分布式协调框架的使用方法、内部实现及运维技巧,旨在帮助读者全面了解ZooKeeper,并更好地使用和运维Zoo......一起来看看 《从Paxos到Zookeeper》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码