前言
假设阅读本文的你已经具备初级前端基础,了解作用域链
,执行环境
,全局变量
,活动变量
等概念。(此处很想放链接,但是我手残还没写相关的解释文章,童鞋们自己搜一下吧)
先定义:
闭包是指:有权限访问另一个函数作用域中的变量的函数。
而要想了解闭包的概念,首先要对函数的执行流程有一定的了解。
- 函数的执行过程
以该函数为例:
function test (i, j) {
return i + j;
}
var result = test(1, 2);
首先,在创建test()
函数时,就会生成一个包括全局变量对象的初始作用域链,这个作用域链保存在test()
函数内部的[[scope]]
属性中。
当test()
函数被调用时,会创建一个执行环境和相应的作用域链(通过复制test()
函数的[[scope]]
属性中的值来构建),此后,会创建一个包含arguments
, i
, j
的活动对象,这个活动对象会推入到作用域链的前端。
最后,一般来讲,当函数执行完毕之后,局部的活动对象就会被销毁,内存中只保留全局的变量对象。
这段话看起来比较绕口,但是配合下面这张图看一下,瞬间会感觉特别清晰。
- 闭包
以上是一般的函数执行过程,❗但是对于闭包来讲,情况是稍微不同的❗,以如下函数为例:
function newTest (propertyName) {
return function(obj1, obj2) {
var val1 = obj1[propertyName];
var val2 = obj2[propertyName];
return val1 + val2;
}
}
PS: newTest()
函数内部创建了一个匿名函数,而匿名函数和闭包实际上是两个不同的概念。只是创建闭包的常见方式是通过匿名函数实现。
前面我们已经知道:闭包是有权限访问另一个函数作用域中的变量的函数。
在上面的函数中,可以看到:
var val1 = obj1[propertyName];
var val2 = obj2[propertyName];
中val1
,val2
是访问了外部函数变量propertyName
,而之所以val1
,val2
可以访问到newTest()
中的变量是因为,匿名函数的作用域链中包括了newTest()
的作用域。所以,newTest()
内部的匿名函数的作用域链中,实际上会包含newTest()
的活动对象。
所以,当调用匿名函数时:
// 创建函数
var x = newTest('num');
// 调用函数
var result = x({num: 1},{num: 2}); // result: 3
匿名函数从newTest()
被返回后,它的初始作用域链就会包括:①全局变量对象、②newTest()
的活动对象。而且在newTest()
被返回后,newTest()
的执行环境的作用域链会被销毁,但newTest()
的活动对象仍然会留在内存中,直到匿名函数被销毁后,newTest()
的活动对象才会被销毁。例如:
// 解除匿名函数的引用,才会释放内存
x = null;
或许看文字还是容易混乱,配合图片理解更下饭:
由于闭包会包括其他函数的作用域链,因此闭包会比其他函数更占内存,建议慎重使用。
✍后记:
本文知识点主要来源于红宝书,此篇也算是自己的笔记吧~
如果有小蒙理解错误的地方,请各位指点一下,感激不尽!❤❤❤