JavaScript 闭包

标签: 闭包  作用域链  执行期上下文

这篇算接上一篇:【JavaScript 预编译:函数声明提升,变量声明提升

这两篇都是因为看了这个视频,觉得讲的挺清楚的就记录下来了


说闭包之前我们先介绍几个概念:

#####1. 执行期上下文
当函数执行时(实际上是函数执行前一刻)会创建一个称为执行期上下文的对象(就是上一篇介绍的 Activation Object),一个执行期上下文定义了一个函数执行期间的环境。当函数执行完毕,它所产生的执行期上下文被销毁。

函数每次执行时对应的执行期上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行期上下文。

#####2. 作用域( [[scope]] )
每个 JavaScript 函数都是一个对象,对象中有些属性我们可以访问,但有些不可以。不可以访问的这些属性仅供 JavaScript 引擎存取,[[scope]] 就是其中一个。

[[scope]] 指的就是我们所说的作用域,其中存储了执行期上下文的集合。

#####3. 作用域链
[[scope]] 中所存储的执行期上下文对象的集合呈链式连接,我们把这种链式连接叫做作用域链。查找变量就是从作用域链的顶端依次向下查找。


下面来看一个例子:

function a() {
	function b() {
		var b = 234;
		console.log(b);
	}
	var a = 123;
	b();
	console.log(b);
}
console.log(a);
var glob = 100;
a();
  1. 函数 a 被定义时发生如下过程:(此时 a 在全局的执行环境中,a 的作用域链上只有一个 GO 对象)
    其中 Global Object 中还应该有 window,document 等对象,这里暂且不管,下同。
    这里写图片描述

  2. 函数 a 执行时发生如下过程:(a 执行的前一个会创建一个它自己的执行上下文 AO 对象,并且将这个对象放在作用域链的最顶端)
    其中 Activation Object 中还应该有 arguments 等,这里暂且不管,下同。
    这里写图片描述

  3. 函数 b 被定义时发生的过程跟函数 a 执行时发生的过程是一样的,此时 b 在 a 的执行环境中。(此时 b 的作用域链上的 AO 对象跟 a 的作用域链上 AO 对象是同一个,都是 a 的)
    这里写图片描述

  4. 函数 b 执行时发生的过程如下:(b 执行的前一个会创建一个它自己的执行上下文 AO 对象,并且将这个对象放在作用域链的最顶端,其他对象依次下移)
    这里写图片描述

当函数 a 函数 b 执行完成后,他们所产生的执行期上下文全部都会被销毁。


再看一个例子:

function a() {
	function b() {
		var bbb = 234;
		console.log(aaa);
	}
	var aaa = 123;
	return b;
}
var glob = 100;
var demo = a();
demo();
  1. 函数 a 被定义时发生如下过程:
    这里写图片描述

  2. 函数 a 执行时发生如下过程:
    这里写图片描述

  3. 函数 b 被定义时发生的过程跟函数 a 执行时发生的过程是一样的。
    这里写图片描述

  4. 函数 demo/b 执行时发生的过程如下:
    值得注意的是,在函数 a 执行完成后将函数 b 作为返回值赋给了全局变量 demo。函数 a 执行完成后会销毁自己的执行期上下文,但是被保存到外部的函数 b 的作用域链上还是能够访问到 aaa。所以当 demo 执行的时候也就是 b 执行的时候,能够输出 aaa 的值 123。 这就是我们说的闭包。
    这里写图片描述


闭包

当内部函数被保存到外部时,仍然能够访问到原来包含函数内部的变量,就会形成闭包。
闭包会导致原有作用域链不释放,造成内存泄漏。(内存泄漏就是,内存被占用的越来越多,可供使用的越来越少。)

闭包为什么会造成内存泄漏?(这个纯粹是我个人的理解,不知道对不对)
js 垃圾收集机制的原理是找出那些不再继续使用的变量,释放其占用的内存。
按照上面的例子来说,函数 b 执行完成之后本应该释放其作用域链,但是由于函数 b 总是被 变量 demo 引用,所以 js 就认为函数 b 一直是有用的,一直不回收它。因此造成了内存泄漏。

闭包的作用:
  1. 实现公有变量;eg:函数累加器
  2. 可以做缓存(存储结构);eg:eater
  3. 可以实现封装(属性私有化);eg:Person
  4. 模块化开发(防止污染全局变量)

前两个作用对应的例子:

eg:函数累加器

function add() {
	var count = 0;
	function fnCount() {
		count++;
		console.log(count);
	}
	return fnCount;
}
var counter = add();
counter();
counter();
counter();
counter();
counter();
...

或者

var counter = null; 
function add() {
	var count = 0;
	counter = function() {
		count++;
		console.log(count);
	}
}
add();
counter();
counter();
counter();
counter();
counter();
...

eg:eater

function eater() {
	var food = '';
	var handFood = {
		eating: function() {
			if(food){
				console.log('I am eating ' + food);
			}
			else{
				console.log('Nothing to eat!');
			}
		},
		pushFood: function(myfood) {
			food = myfood;
		}
	};
	return handFood;
}
var myEater = eater();
myEater.eating();
myEater.pushFood('banana');
myEater.eating();
版权声明:本文为sansan_7957原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sansan_7957/article/details/80783629