• 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
  • 相关阅读:
    webstorm 2017 激活破解
    落在纸上的思考
    Mysql数据库备份脚本
    asp.net c# 打开新页面或页面跳转
    转正专业考试
    关于mysql-5.7.23-winx64.msi的安装
    win server 2012 R2 你需要先安装 对应于 KB2919355 的更新
    零碎
    按钮
    猪猪公寓——冲刺第一天
  • 原文地址:https://www.cnblogs.com/zhihaospace/p/12003828.html
Copyright © 2020-2023  润新知