
麦子学院 2017-08-27 22:48
JavaScript中闭包的本质 Javascript中闭包的本质是什么?
回复:0 查看:2398
闭包是JavaScript
开发人员常常谈论的问题,大家普遍对闭包的认知如下:
模糊的认知:闭包是定义在函数内部的函数;
清晰的认知:闭包是会保存它引用到的外部变量的特殊函数;
其实在JavaScript
语言中,以上
2
种认知都是错误的;为了帮助大家正确地认识闭包,现分享出我对闭包的研究和理解,如下:
1. 闭包
我对闭包的定义是:
闭包的标准定义:携带外部变量的函数称为闭包;
我之所以这样对闭包下定义,是因为这个定义几乎适用所有语言的闭包,如:Object-C
、
Swift
、
JavaScript
等等;所以我认为这是较标准的定义;
对于JavaScript
中的闭包虽然符合标准定义,但是由于
JavaScript
语言的一些特性,使得
JavaScript
中的闭包的实现与其它语言(如:
Object-C
、
Swift
)的实现并不一样;
很多人都认为闭包只会携带它内部引用的外部变量,并不会携带没有引用的外部变量,其实这是错误的;可以通过下面的代码证明:
function
outFun() {
var outArg1 = "
外部参数
1";
var outArg2 = "
外部参数
2";
function
outArg3() {
console.log("
外部参数
3");
}
/*
定义闭包
* codeStr
:字符串类型的参数,该参数的值将被当作代码执行
* return
: 返回将
codeStr
作为代码执行的结果;
* */
function
closureFun(codeStr) {
console.log("
闭包引用的变量的值:
",outArg1);
return eval(codeStr); //
返回将
codeStr
作为代码执行的结果;
}
return closureFun;
}
var getValueOf = outFun(); //
获取闭包
var arg2Value = getValueOf("outArg2"); //
尝试获取闭包内没有引用的变量
outArg2
的值;
console.log(arg2Value); //
输出结果为:外部参数
2
var arg3Value = getValueOf("outArg3"); //
尝试获取闭包内没有引用的函数
outArg3
;
arg3Value(); //
输出结果为:外部参数
3
从示例代码中的运行结果中可以看出,对于闭包引用到的外部变量outArg1
和 闭包没有引用到的变量
outArg2
和函数
outArg3
,在闭包执行时都能被正确地访问到,所以闭包会携带所有的外部变量(函数也是变量);
为什么会这样呢?若要理解,还需先了解一下作用域链的知识,下面是我对JavaScript
的作用域链的理解:
2. 作用域链的理解
1.
可以把作用域链理解成是一个栈结构;
2.
每个作用域都有一个作用域对象用于保存在该作用域内创建的变量
(
包括函数
)
,其保存的方式是:在作用域内创建的变量会成为作用域对象的属性;
3.
作用链链保存的是各级作用域对象的引用,其中最近的作用域的作用域对象在最前端,越远的作用域的作用域对象越靠后;
4.
全局作用域的作用域对象是全局对象本身;所以,每个作用域链的最后端都是全局对象的引用;
5.
在全局作用域内创建的变量会成为全局对象的属性的原因:由于
2(
在作用域内创建的变量会成为作用域对象的属性
)
和
4(
全局作用域的作用域对象是全局对象本身
)
,所以在全局作用域创建的变量会成为全局对象的属性;
6.
函数的作用域链是在函数对象被创建时(被定义时)创建的;
7.
每当函数被执行时,都会新创建一个函数的作用域对象,并把该作用域对象推到作用域链的最前端;
8.
第当函数执行结束时,都会把函数的作用域对象从该函数作用链中推出;
3. 闭包的本质
其实闭包携带外部变量的机制并非闭包的特有机制,它是函数的作用域链的一个效应;在JavaScript
中,闭包和普通函数没有任何本质的区别,闭包只是函数在某种使用场景下的一个名字,就好比凶器只是刀在用于行凶时的名字;
JavaScript中的闭包能携带外部变量的原因是:
JavaScript
的函数在被创建时(被定义时)会生成自己的作用域链;该作用域链会保存各级作用域对象的引用,所以
JavaScript
的函数能够访问其外部的所有变量;
说见上文的<
作用域链的理解
>
所以,本质上,JavaScript
中的闭包携带的不是外部变量,而是外部的作用域对象;
来源:
简书