javascript----闭包

闭包是什么

对于这个问题,我只能给你淫一句诗:

" 不识庐山真面目,只缘身在此山中 "

闭包的使用在我们的平时代码的书写和使用中太常见了,我举几个栗子给你瞧瞧!
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; }();

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 前言 总括 :这篇文章使用有效的javascript代码向程序员们解释了闭包,大牛和功能型程序员请自行忽略。 译者...
    KX九五阅读 280评论 0 1
  • 一、闭包有什么用 1、能够在函数外部引用函数内部的变量(变量作用域); 2、让变量的值始终保持在内存中(垃圾回收机...
    你这个人真的是阅读 368评论 0 1
  • 什么是闭包? 有什么作用 闭包是指一个函数可以调用其他函数的变量。最常见的闭包就是一个函数嵌套另一个函数; 闭包的...
    尹萨萨阅读 332评论 0 0
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,808评论 2 17
  • 寒假放假在家,与长久不见的闺蜜小聚,逛吃逛吃,最后,竟不想回家了,于是找了个宾馆畅谈人生理想。 早上九点多被一通电...
    ThinkingTOMATO阅读 808评论 1 1