JS闭包

一.JS闭包

1.定义:

  闭包是一个闭合容器,我们可以认为闭包是一个对象{key:value}

2.闭包形成条件:

缺一不可:

  • 函数嵌套

  • 内部函数引用外部函数局部变量

  • 外部函数调用

3.作用:

  • 延长外部函数局部变量的声明周期

  • 从外部访问函数内部的局部变量

4.闭包缺点:

  • 占用内存

  • 不及时清除会造成内部泄漏

5.闭包示例:

 1 function fun(){
 2   var num = 123;
 3 
 4   return function fun2(){  //函数嵌套 ==》满足1
 5     console.log(num);  //调用外部函数的局部变量num ==》满足2
 6   }
 7 }
 8 
 9 var f = fun();    
10 
11 f();    //外部函数调用 ==》满足3
12 
13 f = null;  //清除防止内存泄漏

分析:

当在var f = fun();执行结束时应该销毁函数fun以及内部变量num,但是由于执行到f()时需要使用fun()的局部变量num,则虽然fun()被销毁了但是内部num一直保留,直到f = null才结束。

6.使用闭包的场景:

  • 解决循环遍历加事件监听的问题

  • 将内部函数返回出来

  • 将函数作为实参传递给另一个函数调用

(1)解决循环事件监听问题:

 1 var btns = document.getElementsByTagName('button');
 2 console.log(btns);//btns为伪数组:具有数组的一般特性,可以下标取值也有length属性,但没有数组的一般方法不能排序等等
 3 
 4 console.log(Array.prototype.slice.call(btns)); //将伪数组转换为一般数组
 5 for (var i = 0; i < btns.length; i++) {
 6   btns[i].onclick = function(){  //异步方法
 7     console.log(i);
 8   }
 9   
10 }

分析:由于页面加载循环执行完毕,当按钮点击触发才会调用异步方法此时i值错误。

 1     var btns = document.getElementsByTagName('button');
 2     console.log(btns); //btns为伪数组:具有数组的一般特性,可以下标取值也有length属性,但没有数组的一般方法不能排序等等
 3 
 4     console.log(Array.prototype.slice.call(btns)); //将伪数组转换为一般数组
 5     for (var i = 0; i < btns.length; i++) {
 6       (function (i) {
 7         btns[i].onclick = function () { //异步方法
 8           console.log(i);
 9         }
10       })(i);
11 
12     }

分析:此时按钮点击后打印i寻找上层作用域i发现正确。

(2)将内部函数返回出来:

 1 function fun(){
 2   var num = 123;
 3 
 4   return function fun2(){  //作为内部函数返回出来
 5     console.log(num);
 6   }
 7 }
 8 
 9 var f = fun();
10 
11 f();
12 
13 f = null;

(3)作为实参传递给另一个函数调用:

 1   <script type="text/javascript">
 2 
 3     function fun(msg,time){
 4       console.log("fun执行开始");
 5       alert("fun执行开始");
 6       setTimeout(function(){
 7         console.log(msg);
 8       },time);
 9       console.log("fun执行结束");
10     }
11 
12     fun("xxx",2000);

补:

[1].同步与异步

  • 同步:

    • 同步会阻塞后续代码运行

    • 同步没有回调函数

  • 异步:

    • 异步不会阻塞代码运行

    • 异步必须有回调函数

[2].使用闭包自定义JS模版

 1 (function(window){
 2 
 3   var str = "abc";
 4   var num = 123;
 5 
 6   function getstr(){
 7     return str;
 8   }
 9 
10   function getnum(){
11     return num;
12   }
13 
14   //将闭包挂载到window的我们自定义的属性myModule上
15   window.myModule ={
16     getstr:getstr,
17     getnum:getnum
18   }
19 
20 })(window)
21 
22   console.log(myModule.getstr());  //abc

7.例子

例1:

 1     var name = "The Window";
 2     var object = {
 3 
 4       name: "My Object",
 5 
 6       getNameFunc: function () {
 7 
 8         return function () {
 9 
10           return this.name;
11         };
12       },
13 
14       bb: {
15         name: "bb",
16         getNameFunc: function () {
17             return this.name;
18 
19         }
20       }
21     };
22     console.log(object.getNameFunc()()); //The Window
23 
24     console.log(window.object.bb.getNameFunc()); //bb

分析:

1. console.log(object.getNameFunc()()); 相当于打印 window.object.getNameFunc()() 由于先执行

 object.getNameFunc() 返回一个匿名函数,再执行 window.xxx() 最后得到this是指向外层的 window.name 

2.同理可得bb

例2:

 1     var name2 = "The Window";
 2     var object2 = {
 3       name2: "My Object",
 4       getNameFunc: function () {
 5         var that = this;   //缓存this
 6         return function () {
 7           return that.name2;
 8         };
 9       }
10     };
11     console.log(window.object2.getNameFunc()()); //My Object

分析:由于形成了闭包导致that没有被释放所以得到My Object

例3:

 1   function fun(n, o) {
 2     console.log(o)
 3     return {
 4       fun: function (m) {
 5         return fun(m, n)
 6       }
 7     }
 8   }
 9   var a = fun(0)//undefined
10   a.fun(1)  //0
11   a.fun(2)  //0
12   a.fun(3) //0
13 
14   var b = fun(0).fun(1).fun(2).fun(3) //undefined,0,1,2
15 
16   var c = fun(0).fun(1)//undefined,0
17   c.fun(2) //1
18   c.fun(3) //1

分析:

a第一次赋值为一个Object对象,然后指针一直不变;b的指针一直改变;c一开始改变一次之后不变

 1 var a = fun(0);  ==>  n=0,o=undefined;  a={fun:function(m){ return fun(m,0)}}
 2 a.fun(1);    ==>  function(1){return fun(1,0)} ==> fun(1,0) ==> n=1,o=0 
 3 a.fun(2);    ==>  function(2){return fun(2,0)} ==> fun(2,0) ==> n=2,o=0 
 4 a.fun(3);    ==>  function(3){return fun(3,0)} ==> fun(3,0) ==> n=3,o=0 
 5 
 6 
 7 var b = fun(0).  ==> n=0,o=undefined;  b={fun:function(m){ return fun(m,0)}}
 8 fun(1). ==> function(1){return fun(1,0)} ==> fun(1,0) ==> n=1,o=0 ==> b={fun:function(m){ return fun(m,1)}}
 9 fun(2). ==> function(2){return fun(2,1)} ==> fun(2,1) ==> n=2,o=1 ==> b={fun:function(m){ return fun(m,2)}}
10 fun(3) ==> function(3){return fun(3,2)} ==> fun(3,2) ==> n=3,o=2 ==> b={fun:function(m){ return fun(m,3)}}
11 
12 var c = fun(0). ==> n=0,o=undefined;  b={fun:function(m){ return fun(m,0)}}
13 fun(1); ==> function(1){return fun(1,0)} ==> fun(1,0) ==> n=1,o=0 ==> b={fun:function(m){ return fun(m,1)}}
14 c.fun(2) ==>  function(2){return fun(2,1)} ==> fun(2,1) ==> n=2,o=1 
15 c.fun(3) ==>  function(3){return fun(3,1)} ==> fun(3,1) ==> n=3,o=1 

例4:

 1   function Foo() {
 2     getName = function () { console.log(1); };
 3     return this;
 4   }
 5   Foo.getName = function () { console.log(2);};
 6   Foo.prototype.getName = function () { console.log(3);};
 7   var getName = function () { console.log(4);};
 8   function getName() { console.log(5);}
 9 
10   //请写出以下输出结果:
11   Foo.getName(); //2
12   getName();  //4
13   Foo().getName();  //1
14   getName();  //1
15   new Foo.getName(); //2
16   new Foo().getName(); //3
17   new new Foo().getName(); //3

分析:

 1 注:
 2 1.对于同名的变量和方法,JS引擎会先将同名函数声明覆盖了同名变量的声明,然后定义同名变量
 3 2.new 必须与函数在一起,生成实例化对象
 4 3.new 与最近的小括号匹配
 5 
 6 function Foo() {...}  ==>  全局Foo函数
 7 Foo.getName = ... ==> Foo函数对象静态方法getName
 8 Foo.prototype.getName = ... ==> Foo原型对象中有getName属性
 9 var getName ==> 全局变量getName
10 function getName(){...}  ==> 全局方法  
11 
12 执行步骤:
13 
14 ==>1.声明全局Foo() 
15 ==>2.声明全局getName()覆盖了getName变量声明
16 ==>3.定义Foo.getName = function () { console.log(2);}; (Foo函数对象静态方法getName)
17 ==>4.定义Foo.prototype.getName = function () { console.log(3);}; (Foo原型对象中有getName属性)
18 ==>5.定义全局getName变量 = function () { console.log(4);};
19 ==>6.执行Foo.getName(); Foo函数对象的静态方法执行 ==> 2
20 ==>7.执行全局getName();  ==> 4
21 ==>8.执行Foo().getName(); Foo函数对象执行完 ==> getName = function () { console.log(1); }; 修改了全局getName() 
22     ==> 返回this指向window ==> 执行window.getName() ==> 1
23 ==>9.执行全局getName(); ==> 1
24 ==>10.执行new Foo.getName(); Foo函数对象的静态方法执行并生成getName类的对象 ==> 2
25 ==>11.执行new Foo().getName(); ==> 先执行new Foo() 的到一个Foo类的对象 
26       ==> Foo类对象.getName() 获得隐式原型对象__proto__的getName方法和Foo.prototype.getName()相同 
27       ==> 3
28 ==>12.执行new new Foo().getName(); ==>先执行内部new Foo()生成实例对象xxx ==>在执行new xxx.getName() 
29       ==>Foo类对象.getName() 获得隐式原型对象__proto__的getName方法和Foo.prototype.getName()相同,
30       同时生成Foo.prototype.getName的对象 ==> 3

 

posted @ 2019-12-07 22:48  All_just_for_fun  阅读(277)  评论(0编辑  收藏  举报