Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

《你不知道的Javascript(上)》阅读笔记(二) #5

Open
Bless-L opened this issue Jul 27, 2016 · 0 comments
Open

《你不知道的Javascript(上)》阅读笔记(二) #5

Bless-L opened this issue Jul 27, 2016 · 0 comments

Comments

@Bless-L
Copy link
Owner

Bless-L commented Jul 27, 2016

《你不知道的Javascript(上)》阅读笔记(二)

我又来了,隔了这么久才写第二篇,拖延症的我~~~

闭包

定义

闭包是个老生常谈的话题了,网上也有一大堆相关的文章,不过既然是笔记,那也简单提一下吧。
先看wiki中闭包的定义:

闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。

看定义,有两个重点,自由变量和函数。那什么是自由变量?
先看个最简单的闭包例子:

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

想必你也在各种文章看到过类似的例子,那我们就把它往定义中套一套。
我在上篇阅读笔记中说过,内层作用域可以访问到外层作用域的变量。没错,这个bar访问到的外部变量a,相对于bar来说就是一个自由变量。这其实也就满足了最广义的闭包定义了,但我们js中通俗意义上的闭包是还要满足后面这句

这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。

离开了创造它的环境。是的,经过foo那条语句的执行,bar已经被返回并赋值到baz中,也就是暴露在了全局作用域中,离开了创造它的环境。
这个被引用的自由变量将和这个函数一同存在。也就是说,变量a(连同整个内部作用域)并不会因为foo被执行完了就消失,而是会保留在内存中。

就像下面的例子

function foo(){
    var a = 2;
    return function bar(){
        console.log(a++)
    };
}
var baz = foo();
baz();//2
baz();//3
baz();//4
baz();//5

bar中执行了a++,你会看到,每次运行baza的值是累加的,而不是2

综上,这个baz就是我们通常所说的闭包了。

常见闭包

一个很常见的例子就是在for循环里使用闭包
如下面

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

你可会以为这段代码是以一秒的间隔输出1~5
但实际上,这段代码在运行时会以每秒一次的频率输出五次6
6是怎么来的?在循环结束后,i的值为6。也就是说,timer里面的打印i的在循环结束后的i
仔细想想也的确如此,setTimeout的回调是在循环结束后才调用的,我们期望每次循环中会有一个i的副本被保存timer中,以至于在后面输出,但事实上for循环并没有提供这样的机制,所以每次输出的i都是在循环结束后的值6i是存在于全局作用域中被共享的。

这个时候可以用闭包解决

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

或者更常见的写法是这样

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

我们用一个立即执行的匿名函数来构造一个封闭的内部作用域,复制一个i的副本,与timer一起构成一个闭包,从而达到每次保存i的值的目的。

这便是闭包的常见用法。

PS:ES6中,我们可以直接用let来代替var,生成块级作用域,达到同样的效果而不用闭包。

总结

无论你懂不懂闭包,我想你的代码中已经有意无意地存在了闭包。

闭包作用有好多,变量封装,私有化;函数嵌套;函数可以很方便地访问到外部变量等。但不合理的应用也会很容易造成内存泄漏,代码混乱等问题。

重点是要理解闭包及其背后的作用域机制,这样才能更好用闭包来为我们服务。

@Bless-L Bless-L changed the title 《你不知道的Javascript(上)》阅读笔记(二) 《你不知道的Javascript(上)》阅读笔记(二) Jul 27, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant