三、函数高级(四)-闭包

一、函数对象隐含属性

	
	函数声明,就相当于创建函数对象。函数对象中有以下隐含属性:		
			
			1、arguments
						1) 在调用函数时,对函数数据预处理阶段进行初始化。
						   arguments赋值为实参列表,并将其添加为执行上下文对象的属性。	
						   
						2) 函数调用时以及调用完毕,arguments会自动初始化为undefined2this
						1) this从广义上来说是函数对象隐藏的属性,
						   但是实质上this是执行上下文对象的属性,而不是函数的属性。
						
						2) 调用函数时,对函数数据预处理阶段初始化。
						   this属性赋值为调用函数的对象。

						   
						   						
			3、__proto__(显示原型)
						1) 默认指向构造函数隐式原型执行的原型对象,即Function.prototype


 			4、prototype(隐式原型)
						1) 默认指向构造函数隐式原型执行的原型对象,即Function.prototype

			
			5[[Scopes]](作用域链属性)
						1) 外部不能访问,只供JS引擎访问。
						
						2) 作用域链在函数编码时就已经确定。
						   作用域链是抽象的概念,而作用域链属性是作用域链具体的代码实现。
						
						3) 作用域链属性是个数组。从索引值0开始查找。
						   默认情况下,在函数声明,即函数对象创建时,索引值0 指向window对象。
						   在闭环函数中,


1.1 函数对象隐含属性
1)arguments、proto、prototype、[[Scopes]] 是函数对象隐含的属性
            function s(){
                var h="!23";
            }

            console.dir(s)

在这里插入图片描述

2)[[Scopes]] 也是是函数对象隐含的属性。但是只给JS引擎访问。

    function sss(){
        var s=12;
        console.log("123")
    }

    console.log("arguments" in sss)
    console.log("prototype" in sss)
    console.log("__proto__" in sss)
    console.log("[[Scopes]]" in sss) 
    

在这里插入图片描述

3)this从广义上来说是函数对象隐含的属性,但是实质上this是函数对象对应的执行上下文对象的属性

    function sss(){
        var s=12;
        console.log("123")
    }

    console.log("arguments" in sss) //true
    console.log("prototype" in sss) //true
    console.log("__proto__" in sss) //true
    console.log("this" in sss)   //false
    

在这里插入图片描述

1.2 arguments属性
1)arguments属性在函数调用时,执行函数体代码前进行初始化的
2)arguments属性在函数调用完毕会自动初始化为null

    function sss(){
        console.log(arguments)
    }
    
    console.log(sss.arguments)
    sss("123");
    console.log(sss.arguments)
    

在这里插入图片描述

1.3 [[Scopes]] 属性( 闭包之 [[Scopes]] 属性 )

	   作用域链是一个抽象的概念,在函数硬编码阶段,作用域链就已经确定。
       作用域链属性是作用域链这个抽象概念的代码实现。
		
	

二、闭包


	
	+++ 闭包的由来
			1、函数执行完毕,函数内部的变量就会被释放。
				但是由于闭包,延长了闭包函数引用外部函数的变量的存储时间。
				
			2、函数作用域是独立的、封闭的,外部的执行环境是访问不了的。
			     但是由于闭包,函数可以访问另外一个函数作用域的变量。	
	
	
	+++ 闭包
			1) 从狭义上来说,闭包就是能够读取其他函数内部变量的函数。
			   函数嵌套,内部函数引用了外部函数的变量,则该内部函数叫做闭包函数。
			
			   
			2) 从广义上来说。闭包就是闭包作用域对象。它只保存闭包函数引用外部函数的变量。
			   该对象被闭包函数的作用域链属性所引用。
						
						
	+++ 产生闭包的条件
			1) 函数嵌套
			2) 内部函数引用了外部函数中的数据。
			3) 执行外部函数。
						即闭包函数被声明,闭包函数对象被创建。



	+++ 闭包的声明周期
			闭包函数定义执行,就会创建闭包。
			闭包创建后,闭包函数不被外部引用,就会死亡。



	+++ 闭包的作用
			1、延长了函数局部变量的生命周期。
					使函数内部的变量在函数执行完毕后,仍然存活在内存中。
					
			2、在函数外部可以直接操作到函数内部的数据。	
			
1.1 闭包
1)创建闭包的条件
>>>>>> 内部函数必须引用外部函数的变量,才会创建闭包作用域对象

内部函数没有引用外部函数的变量则不会创建闭包作用域对象

   
        function s(){
            var a=12;

            function ss(){
               console.log("123")
            }

            return ss;
        }

        var h=s();
        h();

在这里插入图片描述
内部函数引用了外部函数的变量才会创建闭包作用域对象

        function s(){
            var a=12;

            function ss(){
               a++;
               console.log("123")
            }

            return ss;
        }
        
        var h=s();
        h();
     

在这里插入图片描述

>>>>>> 闭包函数定义执行,才会创建闭包
2)闭包的生命周期
	
	闭包的生命周期:
	
		产生: 闭包函数定义执行,就会创建闭包。 
		死亡: 闭包函数对象不被外部引用就会死亡。

>>>>>> 闭包函数定义执行时,才会创建闭包

在调用ff函数后,首先会初始化ff函数数据,
由于变量提升与函数提升。此时闭包函数f定义执行,就会创建闭包。

    function ff(){
        //在执行该行代码时,就已经产生了闭包。
        var a=1;

        function f(){
            a++;
            console.log(a)
        }

        return f;
    }

    var f=ff();

在调用ff函数后,首先会初始化ff函数数据,
由于变量提升与函数提升。变量a与变量f会被提升,但是f的初始化没有被提升。
所以在执行f的初始化时,才会创建闭包。

    function ff(){
        var a=1;
		
		//在执行该行代码时,才会产生闭包。
        var f=function (){
            a++;
            console.log(a)
        }

        return f;
    }

    var f=ff();

>>>>>> 闭包函数对象不会外部引用,就会死亡
    function ff(){
        //在执行该行代码时,就已经产生了闭包。
        var a=1;

        function f(){
            a++;
            console.log(a)
        }

        return f;
    }

    var f=ff();
    f();
	
	//设置f为null。此时闭包函数不被外界访问,成为垃圾对象
	//此时闭包就会死亡。
	f=null;

3)闭包的作用
	
	闭包的作用:	
		1、延长了函数局部变量的生命周期。
				使函数内部的变量在函数执行完毕后,仍然存活在内存中。
				
		2、在函数外部可以直接操作到函数内部的数据。

>>>>>> 闭包的由来

a、函数在调用完后,其内部定义的变量就会被释放。
b、外部不能访问函数内部定义的变量。


        function s(){
            var a=12;
            console.log(a++)
        }

        //函数在调用后,其内部定义的变量就会被释放
        s();

        //外部不能访问函数内部变量
        console.log(a);//报错

>>>>>> 闭包的作用

1)延长了函数局部变量的生命周期
2)在函数外部可以直接操作

        
        function s(){
            var a=12;

            function ss(){
                a++;
                console.log(a);
            }

            return ss;
        }

        var h=s();
        h();
        h();   

在这里插入图片描述

1.2 闭包的应用
1)利用闭包定义JS模块
	
	1JS模块是具有特定功能的JS文件。
	2、所有的数据和函数封装到一个函数中。
	3、只向外暴露一个具有包含n个方法的对象。
	
>>>>>> 方式一

index.js


function myModel(){

    var msg="abc"

    function doSomething(){
        console.log(msg.toUpperCase())
    }


    function doSomething2(){
        console.log(msg.toLocaleLowerCase())
    }


    return {
        "doSomething":doSomething,
        "doSomething2":doSomething2
    }
}

index.html

	 <script type="text/javascript" src="a.js" ></script>
    
    <script>

       var model=myModel();
       model.doSomething();
       model.doSomething2();


    </script>

在这里插入图片描述

>>>>>> 方式二(典型封装)

index.js



(function(){
    var msg="abc"

    function doSomething(){
        console.log(msg.toUpperCase())
    }


    function doSomething2(){
        console.log(msg.toLocaleLowerCase())
    }

	
	//向外暴露
    window.myModel={
        "doSomething":doSomething,
        "doSomething2":doSomething2
    }
})();

index.html

	 <script type="text/javascript" src="a.js" ></script>
    
    <script>

       myModel.doSomething();
       myModel.doSomething2();


    </script>

在这里插入图片描述

3)利用闭包循环添加监听事件
>>>>>> 方式一:普通添加

    var divs=document.getElementsByTagName("div");
    for(var i=0;i<divs.length;i++){

        divs[i].index=i;
        divs[i].onclick=function(){
           console.log("这是第"+this.index+"个按钮")
        }

    }

>>>>>> 方式二:利用闭包来实现

    var divs=document.getElementsByTagName("div");
    for(var i=0;i<divs.length;i++){
        (function(i){
            divs[i].onclick=function(){
                console.log("这是第"+i+"个按钮")
            }
        })(i);
    }
 
1.3 作用域链属性 – [[Scopes]] 属性 【闭包的本质】


	1) 作用域链是一个抽象的概念,在函数硬编码阶段,作用域链就已经确定。
       作用域链属性是作用域链的代码实现。在函数定义执行时确定。
       
	2) 作用域链属性是内部属性,外部无法访问。只供JS内部调用。

	3) 作用域链属性一个指向变量对象的指针列表,它只引用但不实际包含变量对象。
				a、普通函数中作用域链属性只有一个全局执行上下文对象,即window对象。 
	   			
	   			b、闭包函数中作用域链属性从索引值0开始依次是闭包作用域对象、全局执行上下文。	
						1) 闭包作用域对象是我自己定义的概念。
						   闭包作用域对象存储的是闭包函数引用上一级函数的变量。
						
						2) 闭包作用域对象只有在闭包函数引用外部函数变量时才会形成,否则不会。
	
	4) 闭包作用域对象
			a、闭包作用域对象是我自己定义的概念。
			   它只保存闭包函数引用的外部函数的变量。
			
			b、闭包作用域对象只有在内部函数引用外部函数变量时才会形成。
							
	5) 作用域链属性的作用:
			查找变量时,首先在自身作用域对应的执行上下文对象中查找变量,
	   		如果找不到,就到作用域链属性中从索引值0开始依次向上查找。
	   
1)作用域链属性是函数的内部函数,只给JS引擎访问

        function s(){
            var h=12;
        }

        console.dir(s)

在这里插入图片描述

2)作用域链属性一个指向变量对象的指针列表
>>>>>> 普通函数的作用域链属性中只有一个window对象
        function s(){
            var h=12;
        }

        console.dir(s)

在这里插入图片描述

>>>>>> 闭包函数的作用域链属性中从索引值0开始依次是闭包作用域对象、全局执行上下文。


        function s(){
            var h=12;

            function s2(){
                h++;
            }

            return s2;
        }

        var m=s();

        console.dir(m)
      

在这里插入图片描述


        function s(){
            var h=12;

            function s2(){
                var a=12;
                function s3(){
                    a++;
                    h++;
                }

                return s3;
            }

           return s2();
        }

        var m=s();

        console.dir(m)
      

在这里插入图片描述

3)闭包作用域对象
>>>>>> 闭包作用域对象只保存闭包函数引用外部函数的变量,外部函数的其他变量不会保存。
        function s(){
            var h=12;
            var s=122;
            function s2(){
               h++;
            }

           return s2;
        }

        var m=s();

        console.dir(m)
      

在这里插入图片描述

>>>>>> 闭包作用域对象只在内部函数引用外部时才会创建。
  function s(){
            var h=12;
            var s=122;
            function s2(){
              console.log("!23")
            }

           return s2;
        }

        var m=s();

        console.dir(m)

在这里插入图片描述

4)作用域链属性的作用
	
	作用域链是在编码时就已经确定的,而作用域链属性是作用域链的代码实现。

	查找变量:
			1、沿着作用域链查找变量
					查找变量时,首先在自身作用域对象的执行上下文对象查找,
					找不到,就到函数的作用链属性中查找,0开始依次查找。
					
			2、沿着原型链查找对象的属性
			
1.5 常见的闭包
>>>>>> 将函数对象作为另外一个函数的返回值
    function ff(){
        var a=1;

        function f(){
            a++;
            console.log(a)
        }

        return f;
    }
	
	//创建了一个闭包函数,即产生一个闭包作用域对象
    var f=ff();
    f(); //2
    f();//3

	//又创建了一个闭包函数,即产生一个闭包作用域对象
    var f2=ff();
    f2();//2
    f2();//3
    

在这里插入图片描述

>>>>>> 将函数作为实参传递给另一个函数调用
  function showDelay(msg,time){
        setTimeout(function(){
            alert(msg)
        },time)
  }

  showDelay("213",2000)
1.6 闭包的缺点
	
	闭包的缺点:
			闭包的作用就是延长了局部变量的生命周期。
	   		但是如果闭包函数执行完,如果没有释放闭包函数,容易造成内存泄露。
		
	
       
    function s(){
        var a=new Array(10000);

        function ss(){
            console.log(a.length)
        }

        return ss;
    }

    var h=s();
    h();

    h=null; //让闭包函数成为垃圾对象,被回收

三、内存泄露与内存溢出

	
	+++ 内存溢出与内存泄漏
			内存溢出:
					内存溢出是一种程序运行的错误。
					当程序的运行内存超过剩余内存时,就会抛出内存溢出的报错。
			
			内存泄露:
					内存泄露是指占用的内存没有及时释放
					内存泄露积累多了容易造成内存溢出。
							内存泄露是量变,内存溢出是质变。
	
			
	+++ 常见的内存泄露:
			1) 意外的全局变量
			2) 没有及时清理的定时器
			3) 没有及时清理闭包函数
						
3.1 直视JS运行占用的内存
        var obj={};
        for(var i=0;i<1000;i++){
                obj[i]=new Array(1000000)
        }

        console.log(obj.length)

运行上述代码,查看内存,会发现内存一直向上飙升
在这里插入图片描述

3.2 内存泄露与内存溢出
1) 内存泄露是一种程序运行的错误

在这里插入图片描述

2) 内存溢出是指占用的内存没有及时释放

闭包函数在运行完后,没有及时释放,就会造成内存溢出

        function  f(){
            var a=1;

            function f2(){
                a++;

            }
            return f2;
        }
        var h=f();
        h();
3) 内存溢出与内存泄露的关系
	
	内存溢出是量变,内存泄露是质变。
	内存溢出积累多了,就会造成内存泄露。
	
3.3 常见的内存溢出(解决方案)
1)意外的全局变量
        //1.意外的全局变量
        function s(){
            a=new Array(10000)
            console.log(a.length)
        }
		
		//解决方案
        // a=null;
        
2)没有及时清理定时器

        //2.没有及时清理定时函数
        var id=setInterval(function s(){
            console.log("!23")
        },3000)

		//解决方案
        // clearInterval(id)

3)没有及时清理闭包函数

        //3.闭包
        function  f(){
            var a=1;

            function f2(){
                a++;

            }
            return f2;
        }
        var h=f();
        h();
        
		//解决方案
        // h=null;


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值