闭包

闭包初识

  在MDN中闭包的定义为:可以访问自由变量的函数。(所以我们可以将所有的函数看做是闭包)

  在我们平时的开发使用的我们可以对闭包定义为:可以在函数本身作用域以外的地方被调用。

  首先我们应该先了解一个概念,JS的作用域是静态的,不管函数以哪种方式被调用都只访问申明函数时的作用域内的变量

      先看下面的一个小例子:

var a = "outer";
function print(){
    console.log(a);
}
print();
//毫无疑问打印-->outer
var a = "outer";
function print(){
    console.log(a)
}
function log(){
    var a = "inner";
    print();
}
log();
//仍然会打印
-->outer
var a = "outer";
function print(){
    var a = "inner";
    return function(){
        console.log(a);
    }
}
var log = print();
log();
//打印-->inner

  通过红色的定义我们已经知道函数在调用时,会访问声明函数时的作用域内的变量,所以我们第二个函数会输出outer第三个函数会输出inner。

  首先说明第三个函数已经构造除了一个最简单的闭包。我们知道函数print形成了一个独立的块级作用域,里面的变量只能在函数内部使用,并且在正常的情况下,一旦函数调用完成就会回收里面声明的变量,我们在外层作用域不能访问到,但是在实际上我们在第三个函数中通过log函数额调用仍然输出了inner,这已经违背了我们一般情况,这种特殊的情况我们就称为闭包,更加通俗但不准备的定义--可以外层作用域访问到内层作用域的变量,着就是闭包。

闭包使用

  闭包即一个封闭的空间,通过闭包我们形成一个独立的作用域,可以减少变量名称之间的污染。能形成独立作用域的我们最常见的就是函数,所以闭包和函数就是树和叶一样,其实是不分彼此的。而我们经常使用的一个闭包方式就是匿名函数自执行--(function(){--statement--}())

  闭包的一个好处就是可以减少变量的污染,这个是显而易见的:

(function(){
    var name = "zt";
    console.log(name)
}())
(function(){
    var name = "xjj";
    console.log(name);
}())

  我们对某一个模块的操作不会影响到另一个模块。

  在我们平时的开发中同时会通过三种方式来使用闭包:

  1.参数传递

  2.return 一个基本类型或者对象

  3.对对象的属性进行扩展

  我们先看一个经典的闭包问题:

for(var i=0;i<5;i++){
    setTimeout(function(){
        console.log(i);
    },0)
}

  我们想打印出每次循环的i的值,而上面的这段代码显然是不能达到我们的预期的效果的,因为JS是一种运行在浏览器/node环境中一种单线程语言,只有主线程执行完毕之后,才会去执行消息队列中的语句,所以上面的代码我们可以理解为每一次的循环都会创建一个函数,这个函数的功能的输出i的值,只有循环执行完成以后才会调用函数,但是当调用函数的时候i的值已经变成了5,所以会输出5个5。

  为了达到我们预期的效果我们可以使用闭包来完成:

for(var i=0;i<5;i++){
    (function(i){
        setTimeout(function(){
            console.log(i)
        },0)
    }(i))
}

  此时我们预期的效果已经实现-->0,1,2,3,4

  每次循环创建的函数不再去访问我们的变量i而是去访问我们的参数i,每一个函数的参数都是我们当前循环的变量i的值,所以达到了我们预期的效果。

  另一种我们使用的闭包的方式为 return 一个对象

var person = function(){
    var current = new Date();
    var year = current.getFullYear();
    var age = year - 1993;
    return {
        name:"zt",
        age:age
    }
}();

  通过我们完成了一个操作-->声明一个对象var person = {name:"zt",age:age},通过person.age属性我们可以访问到我们函数内部的变量age的值,同时减轻了变量的污染

  最重要的一种使用闭包的方式为,对某个对象进行属性扩展

var person = {};
(function(p){
    function say(){
        console.log("i can say");
    }
    function sport(){
        console.log("i like sport");
    }
    p.say = say;
    p.sport = sport;
}(person))

  通过这种方式我们为person对象扩展了两个方法,我们经常使用的JQ插件就是通过这种方式来进行扩展的。

 

posted @ 2016-09-19 22:43  y丶卿  阅读(175)  评论(0编辑  收藏  举报