浅谈对js的闭包的理解

首先理解闭包就要先理解全局作用域和局部作用域的区别。函数内部可以访问全局作用域下定义的全局变量,而函数外部却无法访问到函数内部定义的局部变量

如果需要得到函数内部的局部变量,只有通过在函数的内部,再定义一个函数。比如:

一、function f1(){
    var a=1;
    function f2(){
        return a;
    }
    return f2;
}
var result=f1();
console.log(result()); //1
二、function f1(){
    var a=1;
    return function(){
        return a;
    };
}
var result=f1();
console.log(result()) //1

上面代码中,两种写法相同,唯一的区别是内部函数是否是匿名函数。函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是JavaScript语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。函数f1的返回值就是函数f2,由于f2可以读取f1的内部变量,所以就可以在外部获得f1的内部变量了。

闭包就是函数f2,即能够读取其他函数内部变量的函数。由于在JavaScript语言中,只有函数内部的子函数才能读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。闭包最大的特点,就是它可以“记住”诞生的环境,比如f2记住了它诞生的环境f1,所以从f2可以得到f1的内部变量。

闭包可以使得它诞生环境一直存在。看下面一个例子,闭包使得内部变量记住上一次调用时的运算结果。

function f1(num) {
    return function() {
        return num++;
    };
}
var result = f1(2);
console.log(result()) //2
console.log(result()) //3
console.log(result()) //4

上面代码中,参数num其实就相当于函数f1内部定义的局部变量。通过闭包,num的状态被保留了,每一次调用都是在上一次调用的基础上进行计算。从中可以看到,闭包result使得函数f1的内部环境,一直存在。

通过以上的例子,总结一下闭包的特点:

1:在一个函数内部定义另外一个函数,并且返回内部函数或者立即执行内部函数。

2:内部函数可以读取外部函数定义的局部变量

3:让局部变量始终保存在内存中。也就是说,闭包可以使得它诞生环境一直存在。

闭包的另一个用处,是封装对象的私有属性和私有方法。

function Keith(name) {
    var age;
    function setAge(n) {
        age = n;
    }
    function getAge() {
        return age;
    }
    return {
        name: name,
        setAge: setAge,
        getAge: getAge
    };
}
var person = Keith('keith');
person.setAge(21);
console.log(person.name); // 'keith'
console.log(person.getAge()); //21

立即调用的函数表达式(IIFE)

通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。

循环中的闭包

一个常见的错误出现在循环中使用闭包,假设我们需要在每次循环中调用循环序号

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

上面代码中,不会符合我们的预期,输出数字0-9。而是会输出数字10十次。

当匿名函数被调用的时候,匿名函数保持着对全局变量 i 的引用,也就是说会记住i循环时执行的结果。此时for循环结束,i 的值被修改成了10。

为了得到想要的效果,避免引用错误,我们应该使用IIFE来在每次循环中创建全局变量 i 的拷贝。

for(var i = 0; i < 10; i++) {
     (function(e) {
         setTimeout(function() {
             console.log(e); //1,2,3,....,10
         }, 1000);
     })(i);
 }
外部的匿名函数会立即执行,并把 i 作为它的参数,此时函数内 e 变量就拥有了 i 的一个拷贝。当传递给 setTimeout 的匿名函数执行时,它就拥有了对 e 的引用,而这个值是不会被循环改变的。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值