闭包是什么
对于这个问题,我只能给你淫一句诗:
" 不识庐山真面目,只缘身在此山中 "
闭包的使用在我们的平时代码的书写和使用中太常见了,我举几个栗子给你瞧瞧!
function foo(){ var name = "LiAo"; function bar(){ console.log(name); } return bar; } var baz = foo(); baz(); // "LiAo"
function wait(msg){ setTimeout(function timer(){ console.log(msg); },1000); wait("Hello ,LiAo");
var a = 2 ; (function IIFE(){ console.log(a); })();
这些栗子都使用了闭包,其实闭包的本质是内部作用域可以访问外部作用域的原则,不管内部函数在哪里被调用,它都能够访问到外部函数的作用域
关于闭包的经典问题
①
for(var i=1;i<=5;i++){ setTimeout(function timer(){ console.log(i); },i*1000); }
这种问题你应该看到过很多遍了吧,毫无疑问,运行时会以每秒一次的频率输出五次6(等setTimeout里面的函数开始运行时,for循环早已完成,i早已置为6,而此时每个回调函数都在引用完成时候的变量i,自然都是输出6),
为了达到预期的效果,有很多种办法
for(var i=1;i<=5;i++){ (function(){ var j = i; setTimeout(function timer(){ console.log(j); },j*1000); })() }
通过一个IIFE来创建作用域,此时每个循环都有一个块级作用域,由最初的共享i变成分别访问自己块级作用域的j,而这个j又保存着执行循环时候i的值,从而得到了正确的结果
for(var i=1;i<=5;i++){ (function(j){ var j = i; setTimeout(function timer(){ console.log(j); },j*1000); })(i) }
其实这种方法跟上面那种是一样的道理,只不过这里采用隐式赋值,还记得吗,js函数的参数是按值传递的,因为将i传给立即执行函数,相当于执行了var j =i;从而保存了到时的i值,得到了正确的结果。
for(var i=1;i<=5;i++){ let j=i; setTimeout(function timer(){ console.log(j); },j*1000); }
这种方法使用了ES6的let标识符,let标识符让所在代码块能独立成为一个作用域,因而保存了正确的值。
上面的方法也可以简化为下面的形式
for(let i=1;i<=5;i++){ setTimeout(function timer(){ console.log(i); },i*1000); }
②
var name = "The Window"; var object = { name:"My Object", getNameFunc:function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()()); //"The Window"
这个问题是匿名函数并没有渠道其包含作用域的this对象
因为每个函数在被调用时候都会自动取得两个特殊变量:this和arguments,这两个变量在函数被调用时动态绑定。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不能直接访问到外部函数中的这两个变量。
解决方案
var object = { name:"My Object", getNameFunc:function(){ var that = this; return function(){ return that.name; } } }
③
内存泄漏问题
function assignHandler(){ var element = document.getElementById("someElement"); element.onclick = function(){ alert(element.id); } }
在这里我们只需要获得元素的id值即可,而elment.id直接引用了元素本身,导致这元素无法被销毁,堆积多了,自然会导致性能下降
解决方案
function assignHandler(){ var element = document.getElementById("someElement"); var id =element.id; element.onclick = function(){ alert(id); } element = null; }
这里我们只需要保存元素的值就行,不用引用该元素,另外,即使闭包不直接引用element,包含函数的活动对象也会仍然保存一个引用,所以有必要把element变量设置为null。
闭包的使用
模仿块级作用域
(function(){ //这里是块级作用域; })();
私有变量
function MyOject(){ var privateVariable = 10; function privateFunction(){ return false; } this.publicMethod = function(){ privateVariable++; return privateFunction(); } }
优点: 每个实例都有自己的私有变量和方法
缺点:针对每个实例都会无必要的创建同一组新方法。
静态私有变量
function(){ var money = 10; Couple = function(name,age){ this.name = name; this.age = age; }; Couple.prototype.getMoney = function(){ console.log("You have "+money+" money at all") return money; } Couple.prototype.storage = function(num){ money += num; console.log("storage success and you only have "+ money+" at all") } Couple.prototype.draw = function(num){ if(money>=num){ money -= num; console.log("draw success and you only have "+ money+" at all") }else{ console.log("Error! You haven't enough money and the money only remains "+money) } } })(); var LiAo = new Couple("LiAo",22); var Lan = new Couple("Lan",21); LiAo.getMoney(); Lan.getMoney(); LiAo.storage(1000); Lan.draw(500); LiAo.getMoney(); Lan.getMoney(); LiAo.draw(300); Lan.draw(800); You have 10 money at all You have 10 money at all storage success and you only have 1010 at all draw success and you only have 510 at all You have 510 money at all You have 510 money at all draw success and you only have 210 at all
这里是我想到的一种应用场景:夫妻共同使用一个银行账户,固然存款是私有的,不能被直接访问的变量,但是夫妻都有方法进行存和取钱。两者的存取都会影响到他们共同的存款即私有变量,静态私有变量例子的每个实例都是共享同一个私有变量的,所以适合用于多个实例共同使用一个变量的情况。而前面的私有变量例子是每个实例都有自己的私有变量,互不影响。
模块模式
模块模式是为单例创建私有变量和特权方法,而在javascript中是以对象字面量的方式来创建单例对象的
var singleton = function(){ var privateVariable = 10; function privateFunction(){ return false; } return { publicProperty:true, publicMethod:function(){ privateVariable++; return privateFunction(); } }; }();
这种模式在需要对单例进行某些初始化,同时又需要维护其私有变量时是非常有用的.
增强的模块模式
增强的模块模式适合那些单例必须是某种类型的实例,同时还必须添加某些属性和(或)方法对其加以增强的情况
如:
var singleton = function(){ var privateVariable = 10; function privateFunction(){ return false; } var object = new CustomType(); object.publicMethod = function(){ privateVariable++; return privateFunction(); } return object; }();
网友评论