在上一篇文章我们对预解释作了概述,在写这篇博文前打算写几个经典案例,考虑到那些案例综合性比较强,也就循序渐进的有了这篇博文,这样对于学习和深入JavaScript也更加容易入手。
序
一同事去面试,面试官问了一道题:你写一个闭包我看下?于是同事火速写出如下代码:
复制代码 代码如下:
function fn(){
alert('Hello JavaScript Closure!!!');//妈蛋,E文本来就不好,找翻译才把闭包单词写出来
}
fn();
然后面试官摇摇头说道:“这怎么能叫闭包呢?”,最终两人争执不下,同事果断走人,面试官什么玩意儿?(本故事纯属虚构,如有雷同纯属巧合)
闭包可能在很多人眼中都是“高大不好上”的技术,可能在很多人眼中只有这样才算得上闭包:
示例1:
复制代码 代码如下:
function fn() {
return function () {
alert('示例1');
}
}
fn()();
示例1 PS:这个看起来不怎么高级,看样子这人水平不咋地哦!
示例2:
复制代码 代码如下:
;(function () {
alert('示例2');
})();
示例2 PS:这个看起来比上一个要高级,而且第一个括号前还加了一个分号,为何加一个分号,好吧我们先把这个疑问留这儿,后面会讲到。
示例3:
复制代码 代码如下:
~function fn() {
alert('示例3')
}();
示例3 PS:这个最高级了,简直吊炸天,我读书少,你们别骗我!
撸主读书不多,仅能写出这三种“闭包”,相信博友们能写出更多更优秀的“闭包”;到此请先暂停我的瞎掰,接下来研究下函数运行的机制,貌似有人已经知道了,肯定是作用域,我真的很不想在标题上再加上这个作用域,这样总感觉差点儿意思,这个几个东西本来都是一起的,为何要重复呢?老习惯,先上代码:
复制代码 代码如下:
var n = 10;
function fn(){
alert(n);
var n = 9;
alert(n);
}
fn();
好简单的说,我们画图(撸主只会用Windows自带的画图软件,若有更好的请博友推荐)来分析下:
分析1
从图中我们看到了两个作用域,一个是window作用域(顶级作用域),一个是fn调用的时候形成的一个私有作用域;那什么是作用域,作用域其实就是代码执行的环境。举个栗子,一个学生他的学习环境是学校,相当于他的作用域是学校,假如这个学生很调皮,晚上经常FanQiang去网吧打游戏,相当于形成了一个私有环境,这个作用域就是网吧。好吧!这个栗子太TM像撸主本人了,不由感叹一句:“少壮不努力,长大干挨踢”。还是回到正题,其实函数fn的定义就是指向一段代码的描述(图中红框),当这个fn调用(图中的绿框)的时候,就会形成一个作用域,当然这个作用域中的代码执行前也会预解释,我是不会告诉你这个作用域是当它执行完毕后会被销毁,这个fn再次调用也会形成一个新的作用域,然后执行前预解释,然后代码执行,最后执行完毕销毁。
理解闭包
我们知道函数被调用在执行的时候会形成一个私有作用域(执行环境),这个私有作用域就是闭包。回头再看看闭包还是传说中的“高大不好上”吗?我们再回头看看第一个面试故事,还有我写的三个示例,它们其实都是闭包,确切的说那三个示例都是闭包的常用形式。
应用场景
现在有这样一个需求:HTML页面中有一个ul标签,ul下面有5个li标签,要求任意点击一个li,弹出被点击的这个li所在的索引(索引从0开始)位置,HTML结构如下:
复制代码 代码如下:
<ul>
<li>列表1</li>
<li>列表2</li>
<li>列表3</li>
<li>列表4</li>
<li>列表5</li>
</ul>
机智的我火速写出如下代码:
复制代码 代码如下:
var lis = document.getElementById('ul').getElementsByTagName('li');
for (var i = 0, len = lis.length; i < len; i++) {
lis[i].onclick = function () {
alert(i);
};
}
最终测试,看是否完美实现这个需求: