JS闭包

JS的闭包是我学习JavaScript的过程中遇到的第一个难题,但也让人觉得很有意思。接下来写一下我对闭包的理解和一些例子。

难以理解的闭包

相信很多人都有这样的经历,在使用JS操作DOM绑定事件的时候,遇到的令人吐血的”bug”,举例如下:

/*
  现在我需要对一组5个div元素绑定事件,使他们被点击的时候弹出当前点击的div在这一组div中是第几个
  在很多人看来,这还不简单?于是飞速写下了代码,代码如下:
*/
var oDiv = document.getElementsByTagName("div"); 

for(var i = 0; i < oDiv.length; i++) {
    oDiv[i].onclick = function() {
        alert(i + 1);
    };
}

然后实践下,WC,怎么都是6,一脸懵逼。我曾经也一脸懵逼,现在我就想说,呵呵呵。。。
要理解闭包,首先得了解JS的作用域和作用域链,JS没有块作用域,而函数可以创建新的作用域,在给每个div绑定点击事件的时候,它们的回调函数是一个函数表达式,如上面所写的。在这些函数表达式中,创建了一个新的作用域,全局无法访问,但它能访问全局,这也是由于JS的作用域链。OK,如果你不理解这一段也没关系,我们接着讨论。
每个回调函数中的i,即alert(i + 1);中的i,由于在函数中并未重新声明变量i,所以此时所用的i都是全局中的变量i,相信这一点不难理解。而函数所创建的作用域是在其被调用时才产生的,通俗点就是函数中的语句在没有调用时是不会执行的。
那么,在JS执行完上面的脚本后,for循环结束后的变量i的值,此时是5。而当我们点击某个div时,执行alert(i + 1); 这时候会沿着作用域链去找这个i,而我们会在全局中找到变量i,它的值现在就是5,所以无论你点击的是哪个div,都是这么做的,所以弹出的都是5+1=6。
接下来我们就使用闭包,来解决它:

var oDiv = document.getElementsByTagName("div"); 

for(var i = 0; i < oDiv.length; i++) {
    //使用立即执行函数,将i作为参数传入,创建了一个新作用域
    //相应的div的点击事件的回调函数中,都有一个变量num,其保存的是每次循环的i的值
    //当点击事件发生,调用的是返回的匿名函数,
    //这个匿名函数可以访问全局的变量,也可以访问立即执行函数作用域中的变量num
    //此时执行alert(num + 1),在其作用域链中找num,而num就是之前保存相应的i的副本
    //所以弹出的值显示正确
    oDiv[i].onclick = (function(num) { 
        return function() {
            alert(num + 1); 
        };
    })(i);
}

解决了这个问题,再来写一个关于闭包的例子。

function Fn(value) {
    return function(value1) {
        var sum = value + value1;
        return sum;
    };
}

var sumFunc = Fn(1);
var sumResult = sumFunc(2);
console.log(sumResult);  //3

sumFunc = null; //解除对返回的匿名函数的引用,利于闭包所占内存的释放

This对象与闭包

话不多说,直接上例子。

var name = "Window";
var obj = {
    name: "CoderHan",
    getName: function() {
        return function() {
            return this.name;
        };
    }
};
alert(obj.getName()());  //"Window"

在这个例子中,当调用obj.getName()时,在getName()函数内部的this是指向obj的,而返回的匿名函数却是暴露在全局中的,即它是全局的一个函数对象,故此时再调用它,即obj.getName()()时,其this指向全局,在浏览器中为window,所以上面弹出的值为”Window”
要解决这个问题,可以这么做:

//若不重写obj对象的getName()方法,要想得到obj.name,可以这么写:
var objName = obj.getName().call(obj);
alert(objName);  //"CoderHan"

//或者重写getName()方法
obj.getName = function() {
    var that = this;
    return function() {
        return that.name;
    };
};
alert(obj.getName()());  //"CoderHan"

下面一个例子也会导致this的指向改变

var name = "Window";
var obj = {
    name: "CoderHan",
    getName: function() {
        return this.name;
    }
};

(obj.getName = obj.getName)();  //"Window"

赋值语句会返回所赋的值,在此处为obj对象的getName()方法,该函数此时被返回到全局作用域中,故调用它时,其内的this指向window,所以返回”Window”。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值