前端之闭包

closure

谈谈闭包

对于JavaScript而言,虽然没有块作用域的概念,但是有函数作用域的概念,若是想从全局环境下去访问一个函数内的局部变量,是办不到的;但是根据JavaScript中的链式作用域的概念,它是可以访问外面的全局变量的,所以,针对一个被定义在函数内的函数,它的父函数中的所有变量也是能够被它访问到的。

* 什么是闭包

闭包(Closure)是让内部函数能够访问外部函数中的变量。
一个最简单的闭包栗子:

function Closure{
 var a = 1;// 在最父函数环境中定义了a
 return function(){
   a = 2;   // 给a赋值,但是在当前作用域中并没有声明a这个变量
 }
}
func = Closure(); // 这时的返回值即是一个函数
func() // 对里面的函数进行调用,这时a的值发生了改变

* 闭包的作用

闭包的作用就是让内部函数能够访问到外部变量,就像上面例子中所演示的样子。
闭包的另一个作用就是让一些变量始终保存在内存中,看下面:

function func(){
 var a = 1;
 function add(){
   a++;
 }
 function ret(){
   return a;
 }
 return {
   ret:ret,
   add:add
 }
}
var f1 = func();
f1.add();
f1.add();
console.log(f1.ret());

在这个栗子中,函数中的子函数add一共运行了两次,将a的值从1改变到了3,由于父函数被赋给了一个全局变量,所以它就一直在内存中,而不是在调用之后被自动清除了。

* 使用闭包的注意点

上文中已经说过,闭包的特点之一,也是最主要的特点就是函数的所有变量都会被保存在内存中,那么这些变量当然也会占用内存,如果闭包使用过度甚至滥用,就会给内存造成很大的负担,可能会造成性能问题甚至造成内存泄漏,所以在使用完成闭包之后一定要手动去清除这块内存,避免内存问题的出现。

一个闭包的实例(封装的汽车对象)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var Car = carSet();
function carSet(){
var speed = 0;
function setSpeed(spe){
speed = spe;
}
function getSpeed(){
console.log(speed);
return speed;
}
function accelerate(){
speed += 10;
}
function decelerate(){
speed -= 10;
}
function getStatus(){
if(speed === 0){
console.log("stop");
return "stop";
}else{
console.log("running");
return "running";
}
}
return{
setSpeed:setSpeed,
getSpeed:getSpeed,
decelerate:decelerate,
getStatus:getStatus,
accelerate:accelerate
}
}

这时候,可以通过下面的方式去访问和设置速度值

1
2
3
4
5
6
7
8
9
10
11
12
Car.setSpeed(30);
Car.getSpeed(); //30
Car.accelerate();
Car.getSpeed(); //40;
Car.decelerate();
Car.decelerate();
Car.getSpeed(); //20
Car.getStatus(); // 'running';
Car.decelerate();
Car.decelerate();
Car.getStatus(); //'stop';
//Car.speed; //error

javascript