关于闭包

闭包函数:声明在一个函数中的函数,叫做闭包函数。

闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。

 

闭包是指一个函数,其可以调用另一个函数作用域中的变量;

闭包只能取得包含函数中任何变量的最后一个值,如:

实例1

function createFunctions(){
  var result = new Array();
  for (var i=0; i < 10; i++){
    result[i] = function(){
      return i;
    };
  }
  return result;
}
var funcs = createFunctions();
for (var i=0; i < funcs.length; i++){
  console.log(funcs[i]()); //10 10 10 10 10 10 10 ...

问题解决:可以通过创建另一个匿名函数,强制让闭包的行为符合预期。

 

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

 

 

实例2

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <ul>
       <li>click me</li>
       <li>click me</li>
       <li>click me</li>
       <li>click me</li>
   </ul>
   <script type="text/javascript">
    var elements=document.getElementsByTagName('li');
    var length=elements.length;
    for(var i=0;i<length;i++){
       elements[i].onclick=function(){
        alert(i);
    }
}
</script>
</body>
</html>

 

因为闭包,点击每一个,弹出的结果都为4;

可以通过创建另一个匿名函数,强制让闭包的行为符合预期。

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>JS 闭包</title>
</head>

<body>
    <ul>
        <li>hello 1</li>
        <li>hello 2</li>
        <li>hello 3</li>
        <li>hello 4</li>
    </ul>
    <script type="text/javascript">
        var elements = document.getElementsByTagName('li');
        var length = elements.length;
        for (var i = 0; i < length; i++) {
            elements[i].onclick = function(num) {
                return function() {
                    alert(num);
                }
            }(i)
        }
    </script>
</body>

</html>

或者

        var lis = document.getElementsByTagName("li");
        for (var i = 0; i < lis.length; i++) {
            (function(i) {
                lis[i].onclick = function() {
                    console.log(i);
                };
            })(i);
        }

或者

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <title>Document</title>
 </head>
 <body>
  <ul>
   <li>click me</li>
   <li>click me</li>
   <li>click me</li>
   <li>click me</li>
</ul>
<script type="text/javascript">
 var elements=document.getElementsByTagName('li');
 var length=elements.length;
 var handler = function(i){
         return function (){
            alert(i);
        }
    }
    for(var i=0;i<length;i++){
        elements[i].onclick= handler(i);
    }

</script>
</body>
</html>

 下面为什么会立即执行呢?

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <title>Document</title>
 </head>
 <body>
  <ul>
   <li>click me</li>
   <li>click me</li>
   <li>click me</li>
   <li>click me</li>
</ul>
<script type="text/javascript">
 var elements=document.getElementsByTagName('li');
 var length=elements.length;
 var handler = function(i){
         // return function (){
            alert(i);
        // }
    }
    for(var i=0;i<length;i++){
        elements[i].onclick= handler(i);
    }


</script>
</body>
</html>

 

闭包的作用:

1. 可以读取函数内部的变量
2. 让这些变量的值始终保持在内存中。 

出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。

那就是在函数的内部,再定义一个函数。

function f1(){
    n=999;
    function f2(){
      alert(n); // 999
    }
  }

在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1 就是不可见的。这就是Javascript语言特有的“链式作用域”结构(chain scope),

子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!

 function f1(){
    n=999;
    function f2(){
      alert(n);
    }
    return f2;
  }

  var result=f1();
  result(); // 999
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,可以在函数外部对函数内部的局部变量进行操作。

使用闭包的注意点

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

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

 

还没懂?再看看https://blog.csdn.net/weixin_43586120/article/details/89456183

posted @ 2018-05-07 17:30  sunmarvell  阅读(201)  评论(0编辑  收藏  举报