JS闭包 内存泄漏 匿名函数

1.闭包:有权访问另一个函数作用域中的变量的函数。

创建闭包的常见方式就是在一个函数内创建另一个函数,通过一个函数访问另一个函数的私有变量

使用闭包的好处:

1.可以读取到函数内部的变量

2.可以让变量的值保存在内存中,不会被垃圾回收机制回收

3.避免全局变量的污染

缺点:

1.闭包会使得函数中的变量都被保存在内存中,会增大内存消耗量,使用不当会导致内存泄露(解决方法就是在退出函数之前,将不使用的局部变量全部删除)。

列子:

var f1=function(){
   var n=1;
nAdd=function(){n+=1}

    function f2(){
      alert(n);
    }

    return f2;

  }

  var result=f1();

  result(); // 1

  nAdd();

  result(); //2

在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是1,第二次的值是2。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

闭包小列子:1.打印索引值

<body>
<ul class="ds">
<li>123</li>
<li>22</li>
<li>333</li>
<li>444</li>
<li>555</li>
</ul>
<script>
  window.onload=function(){
	  var a=document.getElementsByTagName("li");
	  for(var i=0;i<a.length;i++){
		  (function(){
			  var n=i;
			a[i].onclick=function(){
				  alert(n+1);
				  }
			  })(i);
		  }
	  }
</script>
</body>

计数器:

var add = function(){
      var counter = 0;
      return function(){
             return(++counter);
      }
};
add()();   //counter为1
add()();   //counter为1
add()();   //counter为1
var a=add();
a();//1
a();//2
a();//3
var b=add();
b();//1

如果只执行add()。最终的结果就是返回一个函数function(){return(++count)}。

所以要实现++counter,必须得对add()执行一次后的结果(就是函数function(){++count})再次调用,即调用add(),也即add()()。

但是每次调用add()(),执行的顺序就是:

1、将counter 设置为0;

2、返回一个函数 function(){++counter};

3、执行函数function(){++counter}。

每次调用都重置了counter。

而将add()赋值给a或b,则a()每次运行的为function(){++count},满足计数器。赋值给b相当于重新定义了add()给b,所以b()会重新计数。

var add = (function(){
      var counter = 0;
      return function(){
             return(++counter);
      }
})();      //这里add已经是执行过后的函数了。即add指定了函数自我调用的返回值
add();   //counter为1
add();   //counter为2
add();   //counter为3

变量add指定的是函数自我调用的返回值(指定一个函数并立即执行该函数),add实际上就是函数function(){++function}。

父函数只执行一次,每次调用都只执行了子函数。

这是为什么呢?因为只有执行下面这个函数,counter才重置。而这个函数只在第一次给add赋值时执行过一次。以后每次调用add(),都相当于调用function(){return(++counter)}。

function(){
   var counter = 0;

   return function(){
      return (++counter);
   }
}

前面a b函数也是这个道理。

2.内存泄漏:一块被分配的内存既不能被使用,也不能回收,从而影响性能,从而导致程序崩溃。

内存泄漏起因:一般使用技术系统来处理内存,每个对象根据被引用对象多少来计数,当计数为零时,该对象就会被销毁,释放内存。而当两个对象循环引用时,计数始终为1,则永久性地占着内存。而闭包实际上非常容易造成介js对象和dom对象的循环引用。如:

function ex(){
  var element=document.getElementByID("div1");
element.onclick=function(){
  alert("aaaa");
}
}

ex()用匿名函数  创建闭包,该DOM对象的onclick属性引用了匿名函数闭包,而闭包可以引用外部函数example() 的整个活动对象,包括elemnt ; DOM(div1.onclick) ---->JS(element)由此形成了JavaScript对象和DOM对象的隐蔽循环引用。

解决方法:在js代码段运行完之后将形成循环引用的js对象设置为空

function example(){
    var element =document.getElementByID("div1"); //①
    element.onclick = function() {
        alert("This is a leak!"); //②
    }
    element = null; //添加的语句
}

3.匿名函数

定义:简单说就是没有指定函数名的函数。

//单独的匿名函数  会报错    无法运行    也无法调用
//function() {
//  return 'Lee';
//}

//通过表达式的自我执行
(function() {
    alert('Lee');
})();

//把匿名函数赋值给变量
var cat = function() {
    return 'Lee';
};
alert(cat()); //调用

//把匿名函数自我执行的返回值赋值给变量
var box = (function() {
    return 'Lee';
})();
alert(box);
//自我执行匿名函数的传参
(function (num){
    alert(num);
})(100);

猜你喜欢

转载自blog.csdn.net/Q846169253/article/details/81148290