Javascript中的闭包是如何产生的,具体是什么定义?

4 分钟读完

JavaScript闭包

闭包的概念对于接触C家族语言的人来说可能比较陌生,JavaScript之所以有闭包的概念,自然是因为它的一些不同的特性。在解释闭包前,我们需要先来了解一下JavaScript特有的几个特性:

作用域

什么是作用域,考虑如下的c代码:

void foo(){
//作用域A
	int a=1;
	if(a>0){ 
	//作用域B
		int b=2;
	}
	cout<<b; ///=> 'b' : undeclared identifier
}

在这个函数中变量a所属的的作用域是整个foo函数,变量b所属的作用域是if语句块,在if块之外访问变量b会显示未定义,但在if块内可以访问外部作用域的变量。
JavaScript也有它的作用域,但有一点不同的是,它没有块作用域(ES6添加了letconst关键词,可以声明一个块作用域的变量),只有函数作用域,所以对于下面的代码:

//全局作用域
var foo = function(){
	//函数(局部)作用域
	var a=1
	if(a>0){
		var b=2
	}
	console.log(b)  
}

a  ///=> a is not defined  因为a的作用域是foo函数,这里是全局作用域。
foo() ///=> 2  因为b在foo函数的作用域内。

在JavaScript中,使用var声明的就是局部变量(在外部作用域中无效),而不使用var直接声明的变量则是全局变量(在所有作用域中都有效)。内部的作用域可以访问外部的作用域

函数就是对象

在JavaScript中,函数创建时,解释器会将函数相关的所有相关资源(参数,变量,声明)打包成一个对象,还会执行一些其他操作(我们后面在讲)。需要注意的是,对象可以是嵌套的。 ***
理解上面两个特性之后,我们就来看一种特殊的情况,即嵌套函数。来看一段代码:

function greet(){
	//作用域A
	var name="me"
	return function(){
		//作用域B
		console.log( 'Hi,' + name )
	}
}

在这段代码中,出现了两个函数,一个是greet函数,还有一个是返回的一个匿名函数。
greet函数就是一个正常的函数,在创建时,解释器会打包其内部的变量,声明,参数(没声明参数)。这一切都很正常。
但是对于匿名函数来说,它创建了一个作用域B,在作用域A的内部,所以它可以访问作用域A内的变量,如name,而在这个匿名函数中,也确实使用到了name变量,所以在创建该匿名函数后,解析时解释器会将需要用到的变量(name),声明,参数(无)打包成一个对象,毕竟只有这样,才能保证这个函数在调用的时候能够正常执行。
所以如果我们调用时就会出现这种情况:

greet() //=> 返回一个函数(内部的匿名函数)

//下面调用该匿名函数,有几种方法。	
greet()() //=>  Hi,me

getgreet = greet()  //通常情况下,调用greet之后,greet的作用域A就应该被释放了
getgreet()  //=> Hi,me   //但是在这里name还可以在作用域B中访问,好像并没有被释放

这就说明,name变量被解析在匿名函数中了,并且实际上,解析匿名函数时,作用域B会链接到作用域A上,再链接到主调函数(即产生了作用域链),以保证作用域B可以保持对作用域A的访问。

这种在一个函数对象中,链接了外部作用域,就叫做闭包。

更新时间: