今天咱们来讨论一下 javascript 的这个闭包机制吧
曾经 javascript 的这个闭包的问题困惑了我很久很久
看网上那些说法看了半天都是懵的,看了书更加看不懂
过了好一段时间,在我巩固完一些基础知识之后
现在再回来看这个闭包机制,总算搞懂一些了
首先要弄明白闭包机制,必须对作用域这个东西非常熟悉
JavaScript 中,变量的局部作用域是函数级别的。
也就是说,每一个函数都对应一个作用域,每一个 function() { 后面都是一个新的子域
这没有什么好说的,只是值得一提的是,要能够区分函数级作用域和块级作用域的区别
比如在 C、C++、Java 这些语言中,它的局部作用就是块级别的,一对花括号就对应一个子域1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class Demo{
public static String name = "微学苑"; // 类级变量
public int i; // 对象实例级变量
// 属性块,在类初始化属性时候运行
{
int j = 2;// 块级变量
}
public void test1() {
int j = 3; // 方法级变量
if(j == 3) {
int k = 5; // 块级变量
}
// 这里不能访问块级变量,块级变量只能在块内部访问
System.out.println("name=" + name + ", i=" + i + ", j=" + j);
}
public static void main(String[] args) {
// 不创建对象,直接通过类名访问类级变量
System.out.println(Demo.name);
// 创建对象并访问它的方法
Demo t = new Demo();
t.test1();
}
}
而在 javascript 中,它则是函数级别的,下面这个例子可以明确的区分出来1
2
3
4
5
6
7
8
9
10
11
12function rainman(){
// rainman 函数体内存在三个局部变量 i j k
var i = 0;
if ( 1 ) {
var j = 0;
for(var k = 0; k < 3; k++) {
alert( k ); // 分别弹出 0 1 2
}
alert( k ); // 弹出 3
}
alert( j ); // 弹出 0
}
这里拓展一下,其实为什么会这样子呢?
事实上, javascript 里面有一个机制,就是会把所有的变量声明给前置
也就是说,javascript 在执行脚本的时候,会把函数中所有的变量声明放到函数的最前面
这样子的话,自然而然的就能理解 javascript 不存在块级作用域的问题了
明白了 js 的作用域之后,接下来才能进入我们要讨论的正题,闭包
下面丢一个例子1
2
3
4
5
6
7
8
9
10
11
12
13function f(x) {
var temp = x;
return function(x) {
temp += x;
console.log(temp);
}
}
var a = f(50);
//console.log((a);
a(5);
a(10);
a(20);
运行上面的例子,可以看到输出的结果是 55 , 65,85
也就是说,当 f(50) 这一句给运行完了以后,javascript 的回收机制没有去销毁函数 f(x) 中的 temp 变量
所以总结一下,对于上面这个 f(x) 函数,他有以下一些特点
- 它的返回值是一个函数
- 它的返回值函数中调用了其上一级作用域的变量
- 它的 temp 变量因为被其子函数给引用,所以不会被销毁
- 函数外部无法直接访问 temp ,却能在外部通过调用其子函数来修改 temp 的值
根据以上的特点,可以不难发现闭包可以实现一些封装的特性,所以现在的闭包可以用于面向对象的一些封装
对于使用闭包,还有以下几点需要注意一下
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在 IE 中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
- 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
参考资料:
《作用域与闭包:this,var,(function () {})》
学习 Javascript 闭包(Closure)
闭包 - JavaScript | MDN
详解 js 闭包 - SegmentFault
JavaScript 闭包 - 腾讯 Web 前端 IMWeb 团队社区
深入理解 JavaScript 的变量作用域
Java 变量的作用域_微学苑