美文网首页WEB前端程序开发让前端飞程序员
你不知道的javascript之闭包(入门级)

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

作者: ThereThere_0d70 | 来源:发表于2017-03-28 19:28 被阅读0次

闭包是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方面的萌新,有什么不足之处还望大家指正。这次入门笔记就到这里吧,溜了溜了~

相关文章

  • JavaScript----闭包

    javascript之闭包 闭包的概念     闭包(closure)是 JavaScript 的一种语法特性。 ...

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

    闭包是js中一个重要但对小白来说又是很难搞懂的一个概念。希望这篇文章可以给那些像我一样还是小白的同学一个入门,先给...

  • 闭包

    原文出处 JavaScript深入之闭包 定义 MDN 对闭包的定义为: 闭包是指那些能够访问自由变量的函数。 那...

  • 前端菜鸟帝都一月面试记(答案)

    原文见前端菜鸟帝都一月面试记 闭包和继承,手写继承的几种方式。 闭包看《你不知道的javaScript》上面说,闭...

  • 学习JavaScript闭包和作用域笔记

    JS JavaScript闭包和作用域 闭包 JavaScript高级程序设计中对闭包的定义:闭包是指有权访问另外...

  • 你不知道的JavaScript之闭包

    什么是闭包 《JavaScript高级程序设计第三版》:闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的...

  • 你不知道的javascript--作用域闭包-3

    声明:以下内容摘自《你不知道的javascript》上卷一书的第5章的片段。 1.循环和闭包 要说明闭包,for循...

  • JavaScript之闭包

    闭包 闭包:可以用一个函数 去访问 另外一个函数的内部变量的方式 闭包传参

  • JavaScript之闭包

    闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。闭包导致的问...

  • JavaScript之闭包

    从一个函数的实现说起 话说,我要写这么一个函数 getCallCounter , 该函数无参数,返回一个数字,第一...

网友评论

    本文标题:你不知道的javascript之闭包(入门级)

    本文链接:https://www.haomeiwen.com/subject/pvhsottx.html