JS闭包问题分析

介于之前在网上看到很多关于javascript闭包的问题,今天刚好在学习vue的时候也用到了,借此机会做一个笔记,留下来以后也好重新复习一下,巩固一下基础知识;

介绍一个东西之前,还是先要了解一下这是个什么?

在认识闭包之前,需要了解一个变量作用域的问题;

变量作用域无非是两种:全局变量和局部变量;

var a='koala';

function f(){

    var b='alecor';

    alert(b);
}
f();//alert(b)==>alecor

简单的列子:那么这里的’a‘就是全局变量,‘b’就是局部变量
最后会弹出 ”b“这个变量的值;(这里有必要提一下,就是alert这个函数它是一个同步函数,这就意味着它单击确定后,其后的操作才能进行,否则会一直等待下去)

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

但是你们看,下面这种情况他就不会出现错误,原因是找不到”n”这个变量(这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!)

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

问题来了,如何才能访问内部变量呢???
其实也很简单,我们可以把内部变量当作函数的返回值传递出去,这样外部函数就可以范围访问内部变量了;


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

其实这里面的 f2()函数,就是一个闭包
我们来看看它是如何定义的:

闭包是指可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)(来自百度)

这样理解肯定很晦涩难懂,我的理解是,闭包就是能够读取其他函数内部变量的函数。

我们接下来再看一下例子(可能跨度有些大)

var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name;
      };
    }
  };
  alert(object.getNameFunc()());

其实这道题是我当初我去面试JavaWeb工程师的时候,碰到的一个题,当初就没系统的学习javascript,一直到现在才慢慢把javascript系统的学习,毕竟自己以后是想从事一个全栈,期待全栈后面的世界又会有什么在等着我???

接着看这个题目,
我们来分析一下,为了方便 我直接在备注中写分析

var name = "The Window";  全局变量
  var object = {        //对象,拥有name属性和getNameFunc函数
    name : "My Object",  //内部变量
    getNameFunc : function(){ 
      return function(){  //第一层返回作用域(object对象)
        return this.name;  //第二层返回作用域(window对象)
      };
    }
  };
  alert(object.getNameFunc()());
 这个函数最后输出的是最外一层,因为它的作用域是全局对象,因此调用最外一层的name属性

让我们在看这个列子:

 var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      var that = this;
      return function(){
        return that.name;
      };
    }
  };
  alert(object.getNameFunc()());
在这个示例里面,  我们把object对象的this,赋值给that对象,那么此时that对象存在getNameFunc函数的局部变量,因此最后输出的回事 "myObject"

接着我们看一个关于计时器闭包的问题;

for(var i=0;i<5;i++){ setTimeout(function(){ alert(i); },i*100);
    }

在上述的代码中,你认为他会输出什么,通常来说你肯定以为他会输出,0,1,2,3,4 (间隔一秒钟)

但是其实这是错的,他会连续输出5个5为什么呢???
我们仔细阅读代码看看

setTimeout(function(){
            alert(i);
        },i*100);

在这段定时器的代码中,你能找到的的参数吗? 里面的alert(i)这个i变量不就是得吗???其实并不然;
我们知道函数是可以传递参数的,但是在这个定时器的函数里面它没有给形参,也就是说定时器在调用这个函数的时候它是没有参数的,也可以说他没有局部参数;因此这里的函数仅仅只做在定时器里面的函数声明使用,并没有调用;
所以最后他会输出 5 5 5 5 5,
这里还有一点就是 定时器它是一个异步函数,它必须要等线程队列中没有主线程才会执行它自身,或者说是等队列里面的主线程执行完了,定时器才会作用;所以等for循环走完之后,定时器才开始计时,由于Javascript语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量;所以for循环完之后,找到它的上一级i,也就是5,因此他会连续输出5个5;(需要好好理解)

那么怎么让他一次输出0-1-2-3-4呢???,我这里给出两种方式参考

第一种:函数当作结果值返回

var createFunction=function(i){ 
        return function(){
            alert(i);
        }
    }

for(var i=0;i<5;i++){ setTimeout(createFunction(i),i*1000); }

每一次运行的时候都会给函数传递一个值,此时函数执行完后会返回一个值;

第二种:自执行函数,

for(var i = 0; i < 5; i++) {
        setTimeout(function(i) {
                alert(i)
            }(i), i * 1000);
        }
如何怎么理解自执行函数呢??传送门:[自执行函数](http://blog.csdn.net/limlimlim/article/details/9198111) 

第三种

for(var i = 0; i < 5; i++) {
                (function(i) {
                    setTimeout(function() {
                        alert(i)
                    }, i * 1000);   
            })(i)
        }

这两种方式都是自执行函数,一种放在计时器里面使用,一种放在计时器外面使用;

好了,今晚就介绍这么多,接下来去学习vue2.0了,果然代码是敲出来的,自己思路更加清晰了很多;

你可能感兴趣的:(JavaScript中级篇)