JavaScript闭包.md

闭包

阮一峰学习闭包

闭包的概念

高程上关于闭包的定义:闭包是指有权访问另一个函数作用域中的变量的函数

简单理解就是:定义在一个函数内部的函数

在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

创建闭包的方式:在一个函数内部创建另一个函数

1
2
3
4
5
6
7
8
9
 function f1(){
    var n=999;
    function f2(){
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 999

其中f2( )就是一个闭包能够访问它上一级函数f1()中的变量n。

闭包关于作用域链的图示:

闭包的用途

1、读取其他函数内部的变量

2、让这些变量的始终保持在内存中,这是一个双刃剑,可以方便的访问内部的变量,即使函数执行完成,其内部变量还保存在内存中可以访问,但是也会带来一系列的问题,外部函数的作用域链和变量对象不能销毁,占用内存。

来看一个典型的例子

1
2
3
4
5
6
7
8
9
10
var result = new Array();    
function createFunctions(){
for (var i=0; i < 10; i++){
result[i] = function(){
console.log(i);
};
}
}

result[6](); //10

从表面上看,似乎每一个函数都返回10 ,但实际上,每个函数都返回10, 因为每个函数的作用域链中都保存着父级函数的活动对象,他们的引用的都是同一个变量i,当父级函数return result 后,变量i的值变成10 ,每个函数引用保存变量i的同一个变量对象,因为此时i=10,所以引用变量指向的值都变成10。

解决这个问题最方便的一个方法是将闭包函数立即执行,每次引用都变成实际i对应的值。每次循环立即执行后销毁引用变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
var result = new Array();    
function createFunctions(){
for (var i=0; i < 10; i++){
result[i] = function(){
var c = i;
return function () {
console.log(c);
};
}();
}
}

result[6](); //6

闭包的注意点

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

闭包中的this

匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window

闭包的this对象一般也是指向window。具体使用用要看具体的情况,有没有实例化对象,有没有使用apply() call() 等来改变this对象。

面试中问到关于函数闭包的问题

f(a)(b);

要求返回a+b的结果。

面试的时候面试官写出这样的一个表达式,猛一看这个心想坏了,这是什么表达式,我都没见过,一脸懵逼,一开始以为则是一个立即执行函数,仔细一看不对,这是一个函数执行之后再跟一个()表示执行,还好面试官提示一下,后面的括号就是表示执行函数,那前面的f(a)表示执行结果返回的是一个函数。

接下来就是写这个函数:两种思路,一种我自己想的,一种网上的,当然是晚上的比较好

1、使用闭包
1
2
3
4
5
6
7
function f(x) {
return function(y){
return x+y
}
}
console.log(f(2)); //返回一个函数
console.log(f(2)(3)); //5

这种方法明显的好,使用闭包,函数里面封装一个函数。最为关键的点在于,闭包就是能够读取其他函数内部变量的函数,就相当于把第一次执行时传入的参数x定义为了一个变量,因为在闭包函数中有对x的引用,所以在函数f执行完成之后,内存中x变量的值并没有被清空,在第二次执行的时候可以正常访问到x,并返回x+y。

其实这个问题在阮一峰-关于闭包的学习中有这个表达式,当时自己看的时候只注意到闭包的作用域,没有注意到函数执行表达式。

2、我的方法

我的方法就比较笨, 将闭包特有的性质单独拿出来实现。

1
2
3
4
5
6
7
8
9
10
11
let temp;
function f() {
if (temp) {
return temp * arguments[0]
} else {
temp = arguments[0]
return f;
}
}
console.log(f(2)); //返回一个函数
console.log(f(2)(3)); //5