js4---(闭包与回调函数)

143 阅读1分钟

闭包

闭包:具有作用域的代码块就是闭包

1.闭包的使用场景

1.1

一个函数返回函数内部的工具函数,外部通过工具函数间接访问函数局部变量的过程

    function alipay() {
    	 var money=1000
    	 //不能让函数之外的代码直接操作money  但是可以让自己的内部工具操作 然后返回这个工具
    	 function  tool () {
    	 	money-=20
    	 }
    	return  tool
    }
                
    function  meituan () {
    	// var m=alipay()
    	// m=m-20
    	// console.log(m)
    	var aplitool=alipay()
    	aplitool()
    }
    meituan()
    

1.2

利用函数自调用,每次调用内部的局部变量或者形参都是独立的 来保存一些临时数据

    var arr=[]
    for(var i=0;i<5;i++){			
    	(function(index){
    		arr[index]=function fn(){
    			console.log(index)
    		}
    		// divs[index].onclick=function(){
    		// 	// divs[index]
    		// }
    	})(i)
    }
    arr[2]()//5
    

1.3

利用函数的独立作用域 来生成业务代码块 内部的变量相互不冲突污染全局变量

    (function(){})();
    (function(){})();

2.闭包的优点及缺点

2.1 闭包的优点

1.函数内部的变量 想要被外部程序使用 但是语法决定了外部不能使用,可以利用闭包解决

2.一些临时数据 比如for循环的i的值 希望延时业务中使用 可以使用闭包把临时数据保存到局部作用域中

3.防止变量污染 可以用闭包把一些业务变量放在局部作用域中

2.2闭包的缺点及其解决办法

2.2.1 闭包的缺点

虽然闭包好用 可以解决很多问题 但是使用不当的话就会有一些致命的问题:内存泄漏

内存泄漏:浏览器运行网页 就会执行js代码,引用数据会在内存中占用内存空间,如果有一个对象创建了 而且占用了内存 却没有什么业务使用(想用都用不了) 这种情况就是内存泄漏

系统会定期查看我们的js执行情况,观察创建的对象有没有可能会被使用,如果没有可能 就释放内存,每一个对象都有"人"引用它 如果引用的"人"数为0就释放内存

代码演示:

    引用计数:
    var obj={}//引用计数为1
    var arr=[]
    arr[0]=obj//引用计数为2

    function fn (a) {
    	//var a=obj//引用计数为3
    	var b=a//引用计数为4
    }
    fn(obj)
    引用计数为2

    无内存泄漏:
    function  fn () {
    	var obj={age:20}
    	console.log(123)
    	console.log(123)
    }
    fn()//调用时 内存中有一个对象var obj={age:20}
    调用完毕fn函数,内部的变量没有谁再能访问,对象obj释放,因此没有导致内存泄漏

    内存泄漏:
    function  fn () {
    	var obj={age:20}
    	function  tool () {
    		return obj.age
    	}
    	return tool
    }
    // var re=fn()
    // console.log(re())

    var re=fn()//fn()调用完毕  内部的obj对象没有释放
    console.log(re())
    

2.2.2 内存泄漏的解决办法

内存泄漏的解决方案:

1.尽量避开 不要使用闭包

2.在可能存在泄漏的地方把标识符引用为null

    function fn() {
    	var obj = {
    		age: 20
    	}
    	function tool() {
    		return obj.age
    	}
    	return tool
    }
    var re = fn()
    re=null;

回调函数

先有需求,再设计函数。将外部函数传入内部,是个特殊的闭包.

        function  fn (arg) {
        // console.log(arg)
        // console.log(arg[2])
         // console.log(arg.name)
         // console.log(arg)
        
         var b=100
         arg(b)
    }
    // fn(100)
    // fn("hello")
    // fn([10,20,30])
    // fn({name:'karen'})
    
    function fm(c){console.log(123,c);var a=20;}
    fn(fm)
    //callback==>c钩子函数
    

image.png

案列

function axios(selector,url,callback){
    	console.log(this)
    	var res={tag:selector,data:url}
    	callback("200",res)
    }
    
    
    var obj={
    	el:"#app",
    	mounted:function(){
    		
    		var self=this
    		axios("box1","http://www.baidu.com",function(arg1,arg2){
    			console.log(arg1,arg2,this,self)
    			
    		})
    	}
    }
    obj.mounted()
    

image.png

分析

1.obj.mounted()调用函数,调用者为obj对象。var self=this表示self为obj对象本身

2.axios()开始调用,调用者为window,console.log(this)打印window。

3.callback("200",res)开始调用回调函数function(arg1,arg2){console.log(arg1,arg2,this,self)},调用者为window,arg1=200,arg2=res={tag:"box1",date:"www.baidu.com"} ,this为window,self属性自身没有,去外层作用域寻找var self=this,slfe为obj对象本身