
已解决问题
谷歌21quwqk03用户在2016.05.04提交了关于“南瓜饼J**aScript 匿名函数和闭包介绍”的提问,欢迎大家涌跃发表自己的观点。目前共有1个回答,最后更新于2024-10-24T10:20:55。希望大家能够帮助她。详细问题描述及疑问:期待您的答案,滴水之恩,来日我当涌泉相报 !
详细问题描述及疑问:期待您的答案,滴水之恩,来日我当涌泉相报 !
匿名函数:没有名字
闭包:可访问一个函数作用域里
一匿名函数
12345二闭包
闭包
创建闭
三this对象
12345678910111213141516171819202122232425//在闭包中使用this对象可能会导致一些问题;this对象是在运行时基于函数的执行环境绑定的;//如果this在全局范围就是指向window,如果在对象**就指向这个对象;//而闭包却在运行时指向window的,因为闭包并不属于这个对象的属性或方法;varuser='Window';varobj={user:'Object',getUserFunction:function(){returnfunction(){//闭包不属于obj,里面的this指向window;returnthis.user;};}};console.log(obj.getUserFunction()());//=>Window;//可以强制指向某个对象console.log(obj.getUserFunction().call(obj));//=>Object;//也可以从上一个作用域中的得到对象getUserFunction:function(){varthat=this;//从对象的方法里得到this;此时that指向obj对象;returnfunction(){returnthat.user;}}console.log(obj.getUserFunction()());//=>Object;四内存泄漏
123456789101112//由于IE的JScript对象和DOM对象使用不同的垃圾收集方式,因此闭包在IE中会导致内存泄漏问题,也就是无法销毁驻留在内存中的元素;functionbox(){varoDiv=document.getElementById('oDiv');//oDiv用完之后一直驻留在内存中;oDiv.onclick=function(){alert(oDiv.innerh**TML);//这里用oDiv导致内存泄漏;};oDiv=**ll;//解除引用;}box();//由于匿名函数保存了一个对box()的活动对象的引用,因此就会导致无法减少oDiv的引用数;//只要匿名函数存在,oDiv的引用数至少也是1;因此它所占用的内存就永远不会被回收;//PS:如果没有使用解除引用,那么要等到浏览器关闭才得以释放;五模仿块级作用域(定义并立即调用一个匿名函数)
12345678910111213141516171819202122232425262728293031323334353637383940414243444546//JS没有块级作用域的概念;//这意味着在块语句(for语句/if语句)中定义的变量,实际上是在包含函数中而非语句中创建的;functionbox(count){for(vari=0;i<count;i++){}//box(2);=>count=2;i=2时循环停止,此时i=2;console.log(i);//=>2;i不会因为离开了for块就失效;}box(2);functionbox(count){for(vari=0;i<count;i++){}vari;//就算重新声明,也不会覆盖前面的值;console.log(i);}box(2);//在J**aScript中,变量i是定义在box()的活动对象中的,因此从它有定义开始,就可以在函数**随处访问它;//以上两个例子,说明J**aScript没有块级语句的作用域,if(){}/for(){}等没有作用域;//如果有作用域的话,出了这个范围i就应该被销毁;//J**aScript不会提醒是否多次声明了同一个变量;遇到这种情况,它只会对后续的声明视而不见(如果是初始化并赋值,还是会执行的);//模仿块级作用域(私有作用域)(function(){//这里是块级作用域;})();//以上代码定义并立即调用了一个匿名函数;将函数声明包含在一对圆括号中,表示它实际上是一个函数表达式;//使用块级作用域(私有作用域)改写functionbox(count){(function(){for(vari=0;i<count;i++){}})();console.log(i);//报错,无法访问;变量i在私有作用域中,出了私有作用域即被销毁了.}box(2);//使用了块级作用域后,匿名函数中定义的任何变量,都会在执行结束时被销毁;(i只能在循环中使用,使用后即被销毁);//而私有作用域中能够访问变量count,是因为这个匿名函数是一个闭包,他能够访问包含作用域中的所有变量;//这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数;//一般来说,我们都应该尽可能少向全局作用域中添加变量和函数;过多的全局变量和函数很容易导致命名冲突;//使用块级作用域,每个开发者既可以使用自己的变量,又不必担心搞乱全局作用域;(function(){varbox=[1,2,3,4];console.log(box);//=>[1,2,3,4];box出来就不被认识了;})();//销毁匿名函数中的变量;console.log(box);//=>boxisnotdefined;//在全局作用域中使用块级作用域可以减少闭包占用的内存问题;因为没有指向匿名函数的引用//只要函数执行完毕,就可以立即销毁其作用域链了;六私有变量
123456789101112131415161718192021222324252627282930313233343536//J**aScript没用私有属性的概念;所有的属性都诗用的;//不过有一个私有变量的概念:在任何函数中定义的变量,都可以认为是私有变量,因为不能在函数外部访问这些变量;//私有变量包括函数的参数/局部变量和在函数**定义的其他函数;functionbox(){varage=100;//私有变量,外部无法访问;}//而通过**创建一个闭包,那么闭包通过自己的作用域链也可以访问这些变量;//而利用这一点,可以创建用于访问私有变量的公用方法;特权方法;functionBox(){//构造函数;varage=100;//私有变量;functionrun(){//私有函数;return'运行中';};this.get=function(){//对外公**的特权方法;returnage+run();//将闭包赋值给变量;};}varbox=newBox();console.log(box.get());//可以通过构造方法传参来访问私有变量functionPerson(name){varuser=name;//这句其实可以省略;this.getUser=function(){returnuser;};this.setUser=function(name){user=name;}}varp=newPerson('Lee');console.log(p.getUser());//=>Lee;console.log(p.setUser('Jack'));console.log(p.getUser());//=>Jack;//但是,构造函数模式的缺点是针对每个实例都会创建同样一组新方法;而使用静态私有变量来实现特权方法就可以避免这个问题;七静态私有变量
123456789101112131415161718192021222324252627282930313233//通过块级作用域(私有作用域)中定义私有变量或函数,同样可以创建对外公**的特权方法;(function(){//创建私有作用域;varage=100;//静态私有变量;functionrun(){return'运行中';};Box=function(){};//使用函数表达式定义构造函数;Box.prototype.go=function(){//公有(特权)方法;在原型上定义的;returnage+run();};})();varbox=newBox();console.log(box.go());//100运行中;//上面的对象声明,采用的是Box=function(){}而不是functiongBox(){};并且在声明Box时没有使用var关键字//导致:初始化未经声明的变量,总是会创建一个全局变量;因此,Box就成了一个全局变量,能够在私有作用域之外被访问到;//因为如果用函数声明定义构造函数,那么就变成私有函数了,无法在全局访问到了,所以要使用函数式定义构造方法;(function(){varuser="";Person=function(value){//此处定义的Person是全局变量;user=value;//这里的构造函数有权访问私有变量name;};Person.prototype.getUser=function(){returnuser;};Person.prototype.setUser=function(value){user=value;}})();varperson=newPerson();person.setUser('Lee');console.log(person.getUser());//=>Lee;//使用了prototype导致方法**享了,而user也就变成静态属性了;//所谓静态属性:即**享于不同对象中的属性;?八模块模式
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455//简言之,如果必须创建一个对象并以某些**对其进行初始化,同时还要公开一些能够访问这些私有**的方法,那么就可以使用模块模式;//之前采用的都使造函数的方式来创建私有变量和特权方法;//那么对象字面量方式就采用模块模式来创建;varbox={//字面量对象,也是单例对象:只有一个实例的对象;age:100,//这诗有属性,将要改成私有;run:function(){return'运行中';};};//模块模式私有化变量和函数:varbox=function(){varage=100;functionrun(){return'运行中';}return{//将一个字面量对象作为函数的值返回;go:function(){//返回的对象字面量中只包含可以公开的属性和方法;returnage+run();//由于这个对象是在匿名函数**定义的,因此它的公有方法有权访问私有变量和函数;}};//从本质上讲,这个对象字面量定义的是单例的公**接口;}();//这种模式在需要对单例进行某些初始化,同时又需要维护其私有变量时是非常有用的;//上面直接返回对象的例子,也可以这么写:varbox=function(){varage=100;functionrun(){return'运行中';}varobj={//创建字面量对象;go:function(){returnage+run();}};returnobj;//返回刚创建的对象;}();//字面量的对象声明,其实在设计模式中可以看作是一种单例模式;//所谓单例模式,就是永远保持对象的只有一个实例;//增强的模块模式:适合返回自定义对象,也就使造函数;functionDesk(){};varbox=function(){varage=100;functionrun(){return'运行中';};vardesk=newDesk();desk.go=function(){returnage+run();};returndesk;}();console.log(box.go());//=>100运行中;九小结
在J**aScript编程中,函数表达式是一种非常有用的技术;使用函数表达式可以无须对函数命名,从而实现动态编程;
1.函数表达式
函数表达式不同于函数声明;函数声明要求有名字,但函数表达式不需要;
没有名字的函数表达式叫做匿名函数;2.闭包
当在函数**定义了其他函数时,就创建了闭包.闭包有权访问包含函数**的所有变量;原理如下:
在后台执行环境中,闭包的作用域链包含着它自己的作用域、包含函数的作用域和全局作用域;
通常,函数的作用域及其所有变量都会在函数执行结束后被销毁;
但是,当函数返回了一个闭包时,这个函数的作用域将会一直在内存中保存到闭包不存在为止;3.块级作用域
使用闭包可以在J**aScript中模仿块级作用域(J**aScript本身没有块级作用域的概念);要点如下:
创建并立即调用一个函数,这样既可以执行其中的代码,又不会在内存中留下对该函数的引用;
结果就是函数**的所有变量都会被立即销毁--除非将某些变量赋值给了包含作用域(即外部作用域)中的变量;4.私有变量
闭包还可以用于在对象中创建私有变量,要点如下:
即使J**aScript中没有真是的私有对象属性的概念,但是可以使用闭包来实现公有方法,而通过公有方法可以访问包含作用域中定义的变量;
可以使用构造函数模式、原型模式来实现自定义类型的特权方法,也可以使用模块模式来实现单例的特权方法;