闭包概念

闭包(closure)是 Javascript 语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现
理解闭包,首先必须理解变量作用域

变量的作用域

要理解闭包,首先必须理解Javascript特殊的变量作用域
变量的作用域无非就是两种

  1. 全局变量
  2. 局部变量
    1. var n = 100;
    2. function getNum(){
    3. console.log(n);
    4. }
    5. getNum(); // 100
    另一方面,在函数外部自然无法读取函数内的局部变量
    1. function getNum(){
    2. var n = 100;
    3. }
    4. console.log(n); // error
    ⚠️注意:这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量

如何从外部读取局部变量

出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现
那就是在函数的内部,再定义一个函数
既然getN可以读取getNum中的局部变量,那么只要把getN作为返回值,我们不就可以在getNum外部读取它的内部变量了吗

  1. function getNum(){
  2. var n = 100;
  3. function getN(){
  4. console.log(n);
  5. }
  6. return getN;
  7. }
  8. var result = getNum();
  9. result(); // 100

闭包的概念

上述代码中的getN函数就是闭包
各种专业文献上的”闭包”(closure)定义非常抽象,很难看懂。我们参考老前辈的理解是:闭包就是能够读取其他函数内部变量的函数
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成”定义在一个函数内部的函数
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁

闭包特点

  1. 读取函数内部的变量
  2. 变量的值始终保持在内存中

    读取函数内部的变量

    1. function getNum(){
    2. var n = 100;
    3. function getN(){
    4. console.log(n);
    5. }
    6. return getN;
    7. }
    8. var result = getNum();
    9. result(); // 100

    变量的值始终保持在内存中

    观察多次调用函数,n的变化 ```javascript function getNum(){ var n = 100; n++; console.log(n); }

getNum(); // 101 getNum(); // 101

  1. 增加闭包,在观察n的变化
  2. ```javascript
  3. function getNum() {
  4. var n = 100;
  5. nAdd = function () {
  6. n += 1
  7. }
  8. function getN() {
  9. console.log(n);
  10. }
  11. return getN;
  12. }
  13. var result = getNum();
  14. result(); // 100
  15. nAdd();
  16. result(); // 101

使用闭包的注意点

由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题

闭包应用场景

创建一个函数,声明一个数组,然后向入组中放入一个函数,函数值为i * i

  1. function count() {
  2. var arr = [];
  3. for (var i = 1; i <= 3; i++) {
  4. arr.push(function () {
  5. return i * i;
  6. });
  7. }
  8. return arr;
  9. }
  10. var results = count();
  11. var f1 = results[0];
  12. var f2 = results[1];
  13. var f3 = results[2];
  14. console.log(f1(),f2(),f3()); // 16 16 16

原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了4,因此最终结果为16
返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变

  1. function count() {
  2. var arr = [];
  3. for (var i = 1; i <= 3; i++) {
  4. arr.push((function (n) {
  5. return function () {
  6. return n * n;
  7. }
  8. })(i));
  9. }
  10. return arr;
  11. }
  12. var results = count();
  13. var f1 = results[0];
  14. var f2 = results[1];
  15. var f3 = results[2];
  16. f1(); // 1
  17. f2(); // 4
  18. f3(); // 9

闭包中的this关键字

闭包中的this永远指向window

  1. var name = "The Window";
  2. var object = {
  3. name : "My Object",
  4. getNameFunc : function(){
  5. return function(){
  6. return this.name;
  7. };
  8. }
  9. };
  10. alert(object.getNameFunc()());
  1. var name = "The Window";
  2. var object = {
  3. name : "My Object",
  4. getNameFunc : function(){
  5. var that = this; //this的指向是可以被改变的
  6. return function(){
  7. return that.name;
  8. };
  9. }
  10. };
  11. alert(object.getNameFunc()());