什么是闭包?在实际开发中是如何使用闭包?

一、什么是闭包?

什么是闭包?作为面试常驻题目,相信每一位前端同行都遇到过这个问题。查询网上的一些文章,大多数会告诉你:闭包是一个定义在函数内部的,能够读取其他函数内部变量的函数。诸如此类的回答数不胜数,也许对于有多年工作经验的大佬来说通俗易懂,但是对于我这种才进入行业不久的萌新来说,这段话现的晦涩难懂。
看了许多文章,似懂非懂,大概明白闭包是什么样子的了,但是不明白这种操作有什么意义?在工作中又是如何使用的呢?在作者的努力之下,终于搞明白了这些问题,所以在这里写下笔记,帮萌新解惑,求大佬指点。

二、全局变量和局部变量

要理解闭包,首先要理解JavaScript的全局变量和局部变量。概念什么的就不多赘述了,直接上代码。

    var a = 10//全局变量
	function f1(){
		var a = 10//局部变量
		console.log(a)
	}

通过简单的代码可以直观的看出什么是全局变量和什么是局部变量,那它们的生命周期有什么区别呢?
全局变量自创建开始,就挂载在window对象上面,页面销毁,全局变量也销毁。局部变量则不然,局部变量的销毁和函数f1的执行挂钩,函数开始执行,局部变量创建,函数执行结束,局部变量销毁。这就是全局变量和局部变量的生命周期区别。看如下两段代码:

	var n = 1
	function fn1(){
		n++;
		console.log(n)
	}
	fn1();//2
	fn1();//3
	fn1();//4
	function fn2(){
		var n = 1
		n++;
		console.log(n)
	}
	fn2();//2
	fn2();//2
	fn2();//2

从以上两段代码可以看出,全局变量始终储存在window中,每一次对其的修改都将被保存,但是局部变量不会,每一次调用,都是全新的变量,修改不会保存。
这样在实际开发中对于一些需要长久保留的值我们就需要使用全局变量,但是,全局变量有个缺点,容易造成全局污染
什么是全局污染?如果一个变量是全局变量,那就意味着它可以被任何代码访问和修改,并且在整个程序的生命周期内都存在,如果某个函数修改了一个全局变量,那么它可能会影响其他代码片段的正常运行,即使这些代码片段并不知道它被修改了。这种不可预测的行为可能会导致各种问题,包括程序崩溃、错误结果和难以调试的代码。因此,通常建议避免使用全局变量,尤其是在大型复杂的程序中。
总结来说:局部变量无法共享和长久的保存,而全局变量可能造成全局污染。
那如果我们有一个需求:即要维持一个变量的状态,又不会造成全局污染。这怎么办呢?那就要用到闭包了。

三、闭包的使用

闭包的优点就是可以保护变量,当创建一个闭包时,它会捕获其定义时可用的所有变量,并创建它们的副本。这些变量对于外部代码来说是不可访问的,因此只有通过闭包的特定方法才能访问这些变量。这样就可以保护变量免受外部代码的污染和破坏。看如下代码:

   function fn1(){
		var n = 1
		function f2(){
			n++;
			console.log(n)
		}
		return f2
	}
	var f = fn1()
	f();//2
	f();//3
	f();//4

如此,变量n是局部变量,又兼具了全局变量可以共享保存的优点。同时,如果别的开发者需要使用变量n,他可以再次调用fn1,对变量n进行操作,而不会影响你的代码。如下:

  function fn1(){
		var n = 1
		function f2(){
			n++;
			console.log(n)
		}
		return f2
	}
	//你的代码
	var f = fn1()
	f();//2
	f();//3
	f();//4
	//别人的代码
	var f1= fn1()
	//别人调用
	f1();//2
	//你调用
	f();//5
	f1();//3
	f1();//4

这样函数fn1就成为了一个公共函数,不同的代码独立使用、互不打扰。这就是闭包的使用场景:封装公共函数。将一些公用的代码抽离,抛出入口,供不同的开发者按自己的心意使用。有没有感觉很熟悉?看下一段代码:

Vue.component('my-component', {
  data: function () {
    return {
      message: 'Hello world!'
    }
  },
  template: '<div>{{ message }}</div>'
})

这是一个很简单的vue组件,学过vue的同学都知道,vue组件的data必须是一个函数。因为组件是公用的,假如它的data是一个对象,那意味着data里面的数据状态是共享的,那在当前页面修改了组件data中的数据状态,也会影响到其他调用了这个组件的数据状态。
为了避免这种情况,Vue.js要求data选项必须是一个函数。这样,每个组件实例都将调用这个函数,并返回一个新的对象。这样,每个组件实例都将拥有一个独立的状态,而不会与其他组件实例共享。这就是闭包

四、总结

闭包是能够读取其他函数内部变量的函数,由于在JavaScript中,只有函数内部的子函数才能读取局部变量,所以说,闭包可以简单理解为定义在一个函数内部的函数。
闭包的作用就是保护变量不被污染,在实际开发中,经常使用到闭包的地方就是封装,如:vue组件、网络请求二次封装等。

五、实例

最后,附上一个闭包的实例:

function Http(){
    let xmlhttp = new XMLHttpRequest();
    let _url = "http://localhost:8080/";
    return {
        request:function (method,url,data,success,error){
            xmlhttp.open(method,_url+url);
            if (method === 'GET'){
                xmlhttp.send();
            }else{
                xmlhttp.setRequestHeader("Content-Type","application/json");
                xmlhttp.send(data);
            }
            xmlhttp.onreadystatechange = function (){
                if (xmlhttp.readyState === 4 && xmlhttp.status === 200){
                    success(xmlhttp.responseText)
                }else {
                    error("错误")
                }
            }
        }
    }
}

以上是一个很简单的原生网络请求,利用了闭包的思想进行封装,这样就保护了变量xmlhttp和_url,防止别的代码修改到这两个属性,又可以方便复用,即简洁又安全。
假设我们要调用一个登录的接口就可以这样:

	let http = Http();

	function login(name,password){
		let data = JSON.stringify({username:name,password:password});
		http.request("POST","login",data,function (){
		//成功事件
		},function (){
		//失败事件
		})
	}

Http之所以返回的是一个对象,而不是函数的原因是:方便后来者进行扩展。后来者如果想对Http进行功能扩展,就可以在request对象外随意添加属性进行扩展,这样扩展后的代码就不会与之前的已经使用过request的代码发送冲突。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值