闭包就是函数作用域是一个封闭的区域,作用域变量携带到外层,可以携带它对父作用域的变量的引用,不会被销毁
最近在看 《JavaScript编程精粹》这本书,里面提到高级程序都会使用到闭包
应用场景
1.setTimeout的循环
for(var i = 1; i < 5; i++){ setTimeout(function(){ console.log(i); },i*1000) }
setTimeout 调用的function 是等待以后再去调用的,所以打印了4遍 5 的值。
解决办法:把i值放在封闭的区域里面,function调封闭区域的变量,参数也是局部变量
for(var i = 1; i < 5; i++){ function set(i){ setTimeout(function(){ console.log(i); },i*1000); } set(i); }
输出结果: 1,2,3,4,5
或者写成立即执行,(function(i){ })(i)
都是把i 值丢在一个闭包里面,这样,setTimeout延迟执行后读的 i 也是function 里面的局部变量,因为i被固定到闭包里面了。 i和i之间不会互相被干扰。
function add() { var counter = 0; return counter += 1; } add(); add(); add();
因为闭包对局部变量的封闭,所以上面执行结果是1, 1, 1
2.利用闭包实现私有变量
function SetObj(){ var value = 2; this.readValue = function(){ return value; } this.add = function(){ value = value + 1; } } var obj = new SetObj(); obj.add(); console.log(obj.readValue()); var obj2 = new SetObj(); obj2.add(); obj2.add(); console.log(obj2.readValue());
因为js里面是不存在私有变量的概念的,java中的private
所以利用闭包,可以让value不能被外部变量读取,也不能被随便更改,只能用作者设定的读取和写好的方法去修改这个变量。这样外部就无法读到这个变量。多个功能的实现,即使每个模块
里面的变量和函数重名也没有关系,因为闭包和闭包之间的变量互相是不干扰的。
jquery里面用到很多的闭包,就是避免外部使用者定义的变量去影响库里面自定义的变量,避免重名的风险。外部对方法的调用,也只能按照库约定的方式调用,并不能从调用的方法里面去影响内部的变量值。
比如我们实现功能,定义一个全局的变量来计数,来统计点击的次数,那别的同事很可能会定义和你一样的变量,改改就不知道莫名其妙的报错了找不到哪里有问题。用闭包把这个方法封装起来,读取的时候也写对外的读取方法,就能避免这个问题。es6也有语法能实现这个功能,但是之前是利用闭包来解决这个问题的
=================================================
像上面的例子的闭包是有一个内存泄漏的问题的。因为闭包(比如function)内部的变量,内部的变量,function在立即执行后值是会被销毁的。但是如果它被全局变量引用了,那它的值就不会被销毁了。而且因为内部变量,能读取外部的值,是因为层层引用链的原因,它自己会携带一个很长的数据链。所以引用这个值不被销毁就算了,它所引用的数据链也不会被销毁的,也就说,这个函数外部只要被它所引用的数据,也全部都不会被销毁。
举个例子
var test; function aset(){ var a = 22; var b = 12; test = function as(){ return b; } } aset();
var all = test();
console.log(test.prototype);
这里面test是一个闭包,它把它上级作用域的b变量携带着走了
查看一下打印的结果
test 的 原型 携带了一个scopes的数据链,显示了b变量 和全局变量 b 是属于closure(闭包)的值。可以看到test它携带的引用数据链值范围。闭包的值,在函数执行完毕也不会被销毁。因为它被test调用到了外部。