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

词法作用域与闭包 #29

Open
lovelmh13 opened this issue Feb 7, 2020 · 0 comments
Open

词法作用域与闭包 #29

lovelmh13 opened this issue Feb 7, 2020 · 0 comments

Comments

@lovelmh13
Copy link
Owner

lovelmh13 commented Feb 7, 2020

说实话,闭包的文章都快烂了,但是依然打算自己写一下,加强自己的理解。

什么是闭包

当函数可以记住并且访问所在的词法作用域。就产生了闭包。就算函数是在当前的词法作用域之外执行。 我觉得这个说法好记又好懂。

闭包都是由哪些组成的呢

从闭包最开始的来源:《The mechanical evaluation of expressions》中,可以看到闭包有两部分组成:

  • 环境部分
    • 环境
    • 标识符列表
  • 表达式部分

对应到 js 就是:

  • 环境部分
    • 环境 (函数的词法作用域,它是执行上下文中的一部分)
    • 标识符列表 (函数中用到的未声明的变量)
  • 表达式部分(函数表达式)
    image

词法作用域

上面提到的词法作用域,其实就是我们通常所说的作用域。JS所采用的作用域就是词法作用域。还有一种作用域叫做动态作用域,不过和我们没关系。

词法作用域就是写代码的时候,把变量和块作用域写在哪儿作用域就是哪儿。(还有欺骗词法作用域)所以要看代码写在哪儿,而不是在哪儿执行。

来看一下这个题,理解上面所说的意思:

let a = 0;
function A () {
	let a = 1;
	add = function () {
		a += 1; 
	} 
	function B () {        
		console.log(a);
	} 
	return B; 
} 
let reserve = A();
reserve();  // ?
add(); 
reserve();  // ?
  1. 第一次let reserve = A();的时候,reserve就是function B
  2. 那么执行第一次reserve();的时候,实际就是执行function B,通过作用域链查找,B里没有afunction A里有变量a = 1,于是,打印1。
  3. 接着执行了add();。注意看,add声明的时候没有写var、let、const,所以它是全局变量,而不是局部变量。那么function里面的a += 1会找到全局作用域下的a = 0吗?实际上,并没有。根据词法作用域,我们可以知道,匿名函数里的a += 1的作用域就在当前这个块里面,向上查找,找到的是function A里面所声明的let a = 1的这个a
  4. 于是function B所打印的a是 1+1 得 2,而不是 0 + 1 得 1了 。

通过上面的例子,应该可以了解词法作用域了。就是函数无论在哪里被调用,也无论怎么调用,他的作用域都只由函数被声明时所处的位置决定。

闭包

说回闭包,刚才的代码其实已经产生了闭包了。就是return Bfunction B的作用域在function A里面,但是由于我们把function Breturn出来了,在全局调用(在自己定义的词法作用域以外的地方执行)了function B,但是function B依然可以访问到他所在的作用域内部,通过作用域链的查找,打印出function A里的let a = 1。这里要区分和简单的作用域查找的区别。在它本身的作用域以外执行的,才是闭包。

简单的说,function B依然有对该作用域的引用,这个引用就叫做闭包。

不只是在函数中返回一个函数可以形成闭包,像是setTimeout这种,也回形成闭包。因为setTimeout的回调函数是在全局执行的,但是他的作用域却是在定义时就确定了的。它依然保留着对内部的引用。

闭包的作用

说到作用,最开始的模块模式,貌似就是用的闭包。有了闭包,就可以开辟一个独立的空间,来存储变量,好让变量不去污染全局,而且还可以在外面拿得到这些变量。

但是因为闭包的存在,就会影响到垃圾回收。本来,函数执行完成后,没有了对变量的引用,可以被回收掉了,但是因为闭包,所以还保留了引用,导致不能被垃圾回收。需要注意合理运用。

参考

  1. 《你不知道的JavaScript》
  2. 《JavaScript高级程序设计》
  3. 《重学前端》 JavaScript执行(二):闭包和执行上下文到底是怎么回事?
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