你不知道的javascript之闭包(入门级)

闭包是js中一个重要但对小白来说又是很难搞懂的一个概念。希望这篇文章可以给那些像我一样还是小白的同学一个入门,先给出闭包的定义:
** 当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。**
先来看一个小李子:

function foo() {
        vara = 2;
        return function bar() {
                console.log(a)
        }
}
var baz = foo();
baz();//2

上面例子中,函数<code>bar()</code>的词法作用域访问了函数<code>foo()</code>的内部作用域,在<code>foo()</code>执行后,其返回值(也就是内部的函数<code>bar()</code>)赋给<code>baz()</code>。打印出<code>baz</code>如下:


我们会认为,当函数<code>foo()</code>执行完成之后,其内部作用域应该被销毁了,但接下来的函数<code>baz()</code>为什么依然访问了<code>foo()</code>的内部变量<code>a</code>呢?
实际上,函数<code>bar()</code>的是在<code>foo()</code>内部声明的,<code>bar()</code>也就拥有涵盖<code>foo</code>作用域的闭包,使得<code>foo()</code>已经执行完毕,后其内部作用域依然存在,以供<code>bar()</code>在需要时访问使用。<code>bar</code>对<code>foo</code>的内部作用域的引用就是闭包。闭包使得函数可以访问自己在定义时的词法作用域。其实,当把函数类型的值进行传递,然后该函数在别处被调用时就会出现闭包。例如,下面的代码大家一定都熟悉:

function saySomething(msg){
        setTimeout(function timer(){
                console.log(msg);
        },1000);
}
saySomething("+1s")

这里就有闭包,把<code>timer()</code>传递给<code>setTimeout(..)</code>,<code>timer()</code>就具有涵盖<code>saySomething(..)</code>内部作用域的闭包,实现对变量<code>msg</code>的引用。在定时器、事件监听器、Ajax请求、跨窗口通信、Web Workers或者任何其他的异步(或者同步)任务中,只要使用了回调函数,实际上就是在使用闭包!
当我还十分懵懂的去理解闭包的时候,书中给了一个经典的循环与闭包的问题:

for(var i = 1;i <= 5;i++){
        setTimeout(function timer(){
                console.log(i)},i*1000);
}

对于这段代码,我单纯地以为会每隔一秒打印出1,2,3,4,5。把这段代码运行一下看看:


脸有点疼。。。下面来分析一下why。打印出了一个5,然后每个一秒打印出一个6,打印出的5是<code>setTimeout</code>的id,与闭包无关,先不管。我们知道<code>setTimeout</code>是异步的,这就需要等待同步代码执行完成才会执行异步代码,这里的同步代码就是<code>for</code>循环啦。好的,先进行<code>for</code>循环,它会把当前<code>i</code>的值赋给<code>i*1000</code>里的<code>i</code>,但是<code>for</code>循环1不可能进到<code>timer</code>里面去给<code>i</code>赋值,这样把<code>setTimeout</code>压入队列时就相当于这样:

setTimeout(function timer(){console.log(i);},1*1000);
setTimeout(function timer(){console.log(i);},2*1000);
setTimeout(function timer(){console.log(i);},3*1000);
setTimeout(function timer(){console.log(i);},4*1000);
setTimeout(function timer(){console.log(i);},5*1000);

等循环完成时,<code>i</code>的值就是6了,队列里的异步代码开始执行就会打印出5个6了。当然,不管干啥,都要更深一点是吧,为什么这样呢?尽管上面的5个<code>setTimeout</code>是在各自的循环迭代中定义的,但是由于是闭包,都是引用了外一层的内部作用域,也就是说共享了同一个<code>i</code>。这个问题提醒我们在循环中要注意闭包的作用域。
既然要注意闭包的作用域,怎么才能让上面的例子每隔1秒出处1~5呢?知道原因解决起来也就方便了,一种方法是利用立即执行函数表达式(IIFA),改写如下:

for(var i = 1;i <= 5;i++){
        (function(j){
                setTimeout(function timer(){
                        console.log(j)},j*1000)
        })(i)
}

如果对ES6熟悉一点,也可以使用<code>let</code>:

for(let i = 1;i <= 5;i++){
        setTimeout(function timer(){
                console.log(i)},i*1000);
}

鉴于我也是个js方面的萌新,有什么不足之处还望大家指正。这次入门笔记就到这里吧,溜了溜了~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容