We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
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
我又来了,隔了这么久才写第二篇,拖延症的我~~~
闭包是个老生常谈的话题了,网上也有一大堆相关的文章,不过既然是笔记,那也简单提一下吧。 先看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中通俗意义上的闭包是还要满足后面这句
bar
a
这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。
离开了创造它的环境。是的,经过foo那条语句的执行,bar已经被返回并赋值到baz中,也就是暴露在了全局作用域中,离开了创造它的环境。 这个被引用的自由变量将和这个函数一同存在。也就是说,变量a(连同整个内部作用域)并不会因为foo被执行完了就消失,而是会保留在内存中。
foo
baz
就像下面的例子
function foo(){ var a = 2; return function bar(){ console.log(a++) }; } var baz = foo(); baz();//2 baz();//3 baz();//4 baz();//5
在bar中执行了a++,你会看到,每次运行baz后a的值是累加的,而不是2。
a++
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都是在循环结束后的值6,i是存在于全局作用域中被共享的。
1~5
6
i
timer
setTimeout
for
这个时候可以用闭包解决
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,生成块级作用域,达到同样的效果而不用闭包。
let
var
无论你懂不懂闭包,我想你的代码中已经有意无意地存在了闭包。
闭包作用有好多,变量封装,私有化;函数嵌套;函数可以很方便地访问到外部变量等。但不合理的应用也会很容易造成内存泄漏,代码混乱等问题。
重点是要理解闭包及其背后的作用域机制,这样才能更好用闭包来为我们服务。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
《你不知道的Javascript(上)》阅读笔记(二)
我又来了,隔了这么久才写第二篇,拖延症的我~~~
闭包
定义
闭包是个老生常谈的话题了,网上也有一大堆相关的文章,不过既然是笔记,那也简单提一下吧。
先看wiki中闭包的定义:
看定义,有两个重点,自由变量和函数。那什么是自由变量?
先看个最简单的闭包例子:
想必你也在各种文章看到过类似的例子,那我们就把它往定义中套一套。
我在上篇阅读笔记中说过,内层作用域可以访问到外层作用域的变量。没错,这个
bar
访问到的外部变量a
,相对于bar
来说就是一个自由变量。这其实也就满足了最广义的闭包定义了,但我们js中通俗意义上的闭包是还要满足后面这句离开了创造它的环境。是的,经过
foo
那条语句的执行,bar
已经被返回并赋值到baz
中,也就是暴露在了全局作用域中,离开了创造它的环境。这个被引用的自由变量将和这个函数一同存在。也就是说,变量
a
(连同整个内部作用域)并不会因为foo
被执行完了就消失,而是会保留在内存中。就像下面的例子
在
bar
中执行了a++
,你会看到,每次运行baz
后a
的值是累加的,而不是2
。综上,这个
baz
就是我们通常所说的闭包了。常见闭包
一个很常见的例子就是在for循环里使用闭包
如下面
你可会以为这段代码是以一秒的间隔输出
1~5
。但实际上,这段代码在运行时会以每秒一次的频率输出五次
6
。6
是怎么来的?在循环结束后,i
的值为6
。也就是说,timer
里面的打印i
的在循环结束后的i
。仔细想想也的确如此,
setTimeout
的回调是在循环结束后才调用的,我们期望每次循环中会有一个i
的副本被保存timer
中,以至于在后面输出,但事实上for
循环并没有提供这样的机制,所以每次输出的i
都是在循环结束后的值6
,i
是存在于全局作用域中被共享的。这个时候可以用闭包解决
或者更常见的写法是这样
我们用一个立即执行的匿名函数来构造一个封闭的内部作用域,复制一个
i
的副本,与timer
一起构成一个闭包,从而达到每次保存i
的值的目的。这便是闭包的常见用法。
总结
无论你懂不懂闭包,我想你的代码中已经有意无意地存在了闭包。
闭包作用有好多,变量封装,私有化;函数嵌套;函数可以很方便地访问到外部变量等。但不合理的应用也会很容易造成内存泄漏,代码混乱等问题。
重点是要理解闭包及其背后的作用域机制,这样才能更好用闭包来为我们服务。
The text was updated successfully, but these errors were encountered: