JavaScript闭包的概念及使用笔记

JavaScript闭包

闭包就是能够读取其他函数内部变量的函数,因为 JS 中,只有函数内部的子函数才能读取局部变量。如果想在一个函数内部也有限权访问另一个函数内部的变量就可以使用闭包,闭包就是用来解决这一需求的,**闭包的本质就是在一个函数内部创建另一个函数。**可以把闭包简单理解成"定义在一个函数内部的函数",闭包就是将函数内部和函数外部连接起来的一座桥梁。

闭包有3个特点:

①函数嵌套函数

②函数内部可以引用函数外部的参数和变量

③参数和变量不会被垃圾回收机制回收

闭包的用途:

1、通过在外部调用闭包函数,可以在函数外部访问到函数内部的变量

2、使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收

 function f1(){
    
    
    var n=999;
    nAdd=function(){
    
    n+=1}
    function f2(){
    
    
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 999
  nAdd();
  result(); // 1000
//在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
//为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后被垃圾回收机制(garbage collection)回收。
//这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

闭包的形式一:函数作为返回值

function a(){
    
    
    var name = 'zhangsan'
    return function(){
    
    
        return name;
    }
}
var b = a()
console.log(b)//zhangsan

在这段代码中,a()中的返回值是一个匿名函数,这个函数在a()作用域内部,所以它可以获取a()作用域下变量name的值,将这个值作为返回值赋给全局作用域下的变量b,实现了在全局变量下获取到局部变量中的变量的值

这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!
function fn(){
    
    
    var num = 1;//fn()外部函数访问不到n
    return function(){
    
    
        var n = 0;//匿名函数内部可以访问num
        //既然匿名函数可以读取fn中的局部变量,那么只要把匿名函数作为fn的返回值,就可以在fn外部读取它的内部变量了!
        console.log(++n);
        console.log(++num);
    }
}

var fn1 = fn();
fn1();// 1 2
fn1();// 1 3 函数执行结束后,num变量继续保留在内存中。
fn1();// 1 4 函数执行结束后,num变量继续保留在内存中。

以上代码中,fn()中的返回值是一个匿名函数,这个匿名函数被赋给了fn1,因为当一个函数执行结束后,函数和它里面的变量都会被摧毁掉。而匿名函数中访问了fn()里的变量num,所以变量num不会被销毁,而fn1()执行结束后,fn1()和里面的变量n都会被销毁,只剩下变量num留在内存中,这就实现了在函数执行结束后,使变量继续保留在内存中。

闭包的形式二:闭包作为参数传递

最后总结一下闭包的好处与坏处

好处

①保护函数内的变量安全 ,实现封装,防止变量流入其他环境发生命名冲突

②在内存中维持一个变量,可以做缓存(但使用多了同时也是一项缺点,消耗内存)

③匿名自执行函数可以减少内存消耗

坏处

①其中一点上面已经有体现了,就是被引用的私有变量不能被销毁,增大了内存消耗,造成内存泄漏,解决方法是可以在使用完变量后手动为它赋值为null;

②其次由于闭包涉及跨域访问,所以会导致性能损失,我们可以通过把跨作用域变量存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响

使用闭包的注意点

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

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

思考题

如果你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了。

代码片段一:

var name = "The Window";
  var object = {
    
    
    name : "My Object",
    getNameFunc : function(){
    
    
      return function(){
    
    
        return this.name;
      };
    }
  };
  alert(object.getNameFunc()());//The Window
//this默认指向Window对象,所以最终输出的结果是全局对象的name,也就是"The Window"

代码片段二:

 var name = "The Window";
 var object = {
    
    
    name : "My Object",
    getNameFunc : function(){
    
    
      var that = this;
      return function(){
    
    
        return that.name;
      };
    }
  };
  alert(object.getNameFunc()());//My Object
//这里的this一开始默认指向的是Window对象,然后var that=this把全局变量变成了局部变量,object.getNameFunc()返回的结果是that.name,也就是局部变量"My Object"

猜你喜欢

转载自blog.csdn.net/m0_48895748/article/details/127047140