Open Bless-L opened 8 years ago
我又来了,隔了这么久才写第二篇,拖延症的我~~~
闭包是个老生常谈的话题了,网上也有一大堆相关的文章,不过既然是笔记,那也简单提一下吧。 先看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,生成块级作用域,达到同样的效果而不用闭包。 总结
PS:ES6中,我们可以直接用let来代替var,生成块级作用域,达到同样的效果而不用闭包。
let
var
无论你懂不懂闭包,我想你的代码中已经有意无意地存在了闭包。
闭包作用有好多,变量封装,私有化;函数嵌套;函数可以很方便地访问到外部变量等。但不合理的应用也会很容易造成内存泄漏,代码混乱等问题。
重点是要理解闭包及其背后的作用域机制,这样才能更好用闭包来为我们服务。
《你不知道的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
的值的目的。这便是闭包的常见用法。
无论你懂不懂闭包,我想你的代码中已经有意无意地存在了闭包。
闭包作用有好多,变量封装,私有化;函数嵌套;函数可以很方便地访问到外部变量等。但不合理的应用也会很容易造成内存泄漏,代码混乱等问题。
重点是要理解闭包及其背后的作用域机制,这样才能更好用闭包来为我们服务。