javascript中的闭包

闭包-javascript

闭包就是:能够读取其他函数内部变量的函数

定义在一个函数内部的函数、
闭包可以将函数内部和函数外部连接起来

闭包用途

延长作用域链,因为闭包可以读取函数内部变量
让这些变量始终保持在内存中
更好的组织代码,比如模块化,异步代码转同步

闭包缺点

增加了内存的消耗,除非手动设置为null
某些浏览器上因为回收机制的问题,有内存溢出风险。
增加了代码的复杂度,维护和调试不便。

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(),因为result是全局,导致f2一直在内存中

而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收

做题帮助理解

  1. 1.


      var name = "The Window";

      var object = {
        name : "My Object",

        getNameFunc : function(){
          return function(){
            return this.name;
          };

        }

      };

      alert(object.getNameFunc()());

输出 the window
object.getNameFunc()为闭包,内容为return this.name,而this指向的是window

  1. 2.


      var name = "The Window";

      var object = {
        name : "My Object",

        getNameFunc : function(){
          var that = this;
          return function(){
            return that.name;
          };

        }

      };

      alert(object.getNameFunc()());

输出 my object
object.getNameFunc()为闭包,内容为return that.name, 而that在上下文执行环境为object

换个题哈

  1. 1.
var name = 'global';

var obj = {
    name : 'obj',
    dose : function(){
        this.name = 'dose';
        return function(){
            return this.name;
        }
    }
}

alert(obj.dose().call(this))

输出 global
var xxx = obj.dose();
xxx.call(this);
xxx为闭包,return this.name,利用call将this指向window

  1. 2.
var name = 'global';

var obj = {
    name : 'obj',
    dose : function(){
        this.name = 'dose';
        return function(){
            return this.name;
        }
    }
}

alert(obj.dose()())

输出 global, 道理同1,相当于直接执行xxx()

  1. 3.
var name = 'global';

var obj = {
    name : 'obj',
    dose : function(){
        this.name = 'dose';
        return function(){
            return this.name;
        }
    }
}

alert(obj.dose.call(this))

输出 function(){
return this.name;
}
obj.dose只是指向dose这个方法,.call(this)将执行方法,返回return 这个函数

4

var name = 'global';

var obj = {
    name : 'obj',
    dose : function(){
        this.name = 'dose';
        return function(){
            return this.name;
        }
    }
}
var xxx = obj.dose;
xxx();
console.log(name);

输出 dose, window下执行xxx,将global改为dose

  1. 5.

var name = 'global';

var obj = {
    name : 'obj',
    dose : function(){
        this.name = 'dose';
        return function(){
            return this.name;
        }
    }
}
obj.dose();
console.log(obj.name);
console.log(name);

输出 dose
global

var name = 'global';

var obj = {
    name : 'obj',
    dose : function(){
        this.name = 'dose';
        return function(){
            return this.name;
        }.bind(this)
    }
}

alert(obj.dose().call(this))

输出 dose

bind(this),里面的this是指的obj, 因为obj.dose()

由于return的function中用了bind,所以相当于固定了this,外边再call什么进来,也只是碍眼法而已。由于函数内部邦定了this,所以此处的情况要另外分析了

首先,obj对象定义了name属性为’obj’;接着在dose 方法内,又改写了name属性为’dose’; 根据作用域链的就近原则,alert访问的肯定就是’dose’这个值了。

再接再厉

  1. db

var db = (function() {
// 创建一个隐藏的object, 这个object持有一些数据
// 从外部是不能访问这个object的
var data = {};
// 创建一个函数, 这个函数提供一些访问data的数据的方法
return function(key, val) {
    if (val === undefined) { return data[key] } // get
    else { return data[key] = val } // set
    }
// 我们可以调用这个匿名方法
// 返回这个内部函数,它是一个闭包
})();

db('x'); // 返回 undefined
db('x', 1); // 设置data['x']为1
db('x'); // 返回 1
// 我们不可能访问data这个object本身
// 但是我们可以设置它的成员
  1. setTimeout
for (var i = 0; i < 3; i++) {
    setTimeout(function() {
        console.log(i);
    }, 0);
    console.log(i);
}

0, 1, 2, 3, 3, 3

setTimeout是异步的
setTimeout()有两个参数,第一个参数是函数,第二参数是时间值。
调用setTimeout时,把函数参数,放到事件队列中。等主程序运行完,再调用。
就像我们给按钮绑定事件一样:

btn.onclick = function() {
        alert(1);
};

这么写完,会弹出1吗。不会!!只是绑定事件而已!
必须等我们去触发事件,比如去点击这个按钮,才会弹出1。

setTimeout也是这样的!只是绑定事件,等主程序运行完毕后,再去调用

setTimeout(fn, 2000)

我们可以理解为2000之后,再放入事件队列中,如果此时队列为空,那么就直接调用fn。如果前面还有其他的事件,那就等待。
继续看:

setTimeout(function() {
        console.log(i);
}, 0);
var i = 1;

程序会不会报错?
不会!而且还会准确得打印1。
为什么?
因为真正去执行console.log(i)这句代码时,var i = 1已经执行完毕了!

现在我们解决setTimeout里console.log问题,
setTimeout里是一个匿名函数,这个函数可以调用全局变量,所以这个函数是个闭包
for循环里每次执行setTimeout都会讲这个匿名函数压入栈,等到主程序运行完再来事件队列中依次执行匿名函数,此时匿名函数访问到的i已经变成3了,所以后面会打印3次3.

解决办法

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

输出:0(for),1,2,0(function),1,2

再创建一个闭包:
var xxx = function(i) {
return function() {console.log(i)}
}
xxx(i);

function f1(i) {
    console.log(i);
}
for (var i=0; i<3; i++) {
    setTimeout(f1.bind(null, i),0);
    console.log(i);
}

输出:0(for),1,2,0(function),1,2

bind返回一个函数,并将参数i传入

但是,注意下面这样输出的结果

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

输出0(f1),0(for), 1,1, 2, 2
原因:setTimeout()中第一个参数是一个函数表达式,在解析时候就已经执行了f1(i),将i打印出来了,而且f1返回的是一个undifine。等到指定时间去检查发现不是function就不再执行

参考:
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
http://www.cnblogs.com/afrog/p/4276709.html
http://www.qdfuns.com/notes/17398/e8a1ce8f863e8b5abb530069b388a158/page/.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值