闭包的概念定义是指有权访问另一个函数作用域中的变量的函数。即在一个函数内部创建另一个函数,内部函数可以访问外部函数的变量和参数。当代码逻辑不是很复杂的时候,是不需要涉及到闭包的。以前对闭包都不是很理解,主要是用的少,所以感觉很神秘。后面使用到class类型的操作更多,导致闭包应用的场景更少了。但是理解闭包,对于程序结构的理解还是很有好处,特别是在一些静态语言里面,仍然很有作用。这里主要是以JS为例子来解释闭包。因为闭包是JS里面几乎必备的概念,特别是ES5以前,对象编程概念很弱的时候,特别需要闭包这种来清晰代码结构。
我们先来实现一个简单的闭包:
//定义一个函数,来使用闭包 假设这段函数很长,可能几千行代码 是我们新写的功能
function $(val) {
//定义一个变量 这个变量主要是给外部的值赋权 也就是乘
let b = 3;
let c = val;
//通过中转函数,统一对外的访问
function loginSuccess(val) {
alert(val)
passalert(b * val);
}
//闭包函数可以利用函数内部的变量,又实现了隔离
function passalert(v2) {
alert(v2)
v2 = v2 + 1;
alert(v2);
}
//返回一个内部函数
return loginSuccess;
};
//我们需要读取函数内部的函数
$()(5); //结果是16
该函数用到了一个闭包,我们定义了一个 的函数,传入了 5 的值,然后调用了 l o g i n S u c c e s s 函数,去调用 p a s s a l e r t 函数,就这样一个简单的调用,逻辑已经有点开始绕。 的函数,传入了5的值,然后调用了loginSuccess函数,去调用passalert函数,就这样一个简单的调用,逻辑已经有点开始绕。 的函数,传入了5的值,然后调用了loginSuccess函数,去调用passalert函数,就这样一个简单的调用,逻辑已经有点开始绕。()其实就是loginSuccess 替换。可以看到闭包的好处:
- 可以创建私有变量:通过在函数内部定义变量,并在闭包中使用,这些变量在外部无法直接访问和修改,增加了数据的安全性和封装性。哪怕一个方法几千行,只要封装在$()里面,最后返回一个我们要的函数,我们将要执行的参数传入进去,外部是无感的。
- 模拟私有方法:实现类似于其他语言中的私有方法的效果,保护内部逻辑不被外部随意修改。显然在值暴露一个参数输入的情况下,我们是不可能修改b的值。
- 实现模块模式:将相关的功能和数据封装在一个模块中,只暴露必要的接口。里面无论叠加再多的逻辑,再多的方法,我们只要保持最终输出方法接收的方法不变化,只暴露出输出的函数,会发现非常安全。
在上面例子里面,我们已经看到了闭包的作用。我们看个更复杂的例子,主要模拟$(“#id”).text这样就可以获取到某个id的值(最简单的jquery情况)。jqeury的底层写的非常复杂
<script>
//再用闭包封装一个$函数 实现能根据$("#id") $(".class") $("img") 获取对应元素的效果
function cs(val) {
//定义一个插件的对象,将我们内部的执行结果全部放入该对象里面
rtobj = {
obj: null,
text: ''
};
//我们再次定义写个简单的函数 三个简单的方法
function getTag(val) {
return document.getElementsByName(val)
}
function getElementsByClassName(val) {
return document.getElementsByClassName(val)
}
function getElementById(val) {
return document.getElementById(val)
}
//我们通过一个对外输出的方法,我们将方法封装到返回函数里面
function rtRes() {
if (val.indexOf('#') == 0) {
rtobj.obj = getElementById(val.slice(1))
rtobj.text = rtobj.obj.textContent
} else if (val.indexOf('.') == 0) {
rtobj.obj = getElementsByClassName(val.slice(1))[0]
rtobj.text = rtobj.obj.textContent
} else {
rtobj.obj = getTag[0]
rtobj.text = rtobj.obj.textContent
}
//返回结果是一个对象,可以读取原始的返回对象,也可以读取我们已经封装了一层的text
return rtobj;
}
//返回的结果模拟最简单的jq效果,也就是以后我们如果开发一个插件库,为了避免外部变量污染,直接用个函数闭包引入即可使用
return rtRes;
};
let $ = cs()
//闭包还可以让函数的内布值保持不变,这意味着改变对象内部变量的时候,很多初始化的变量不会再次被初始化
console.log($("#logo").text)
</script>
从这里也可以看出,如果只是简单操作dom 我们其实不用闭包也可以完成,调用其他库已经提供在底层的组件功能。这也是我们在ES6和TS发展之前主要的插件编写方法。因为在JS里面,对象的开发很不完善,我们创建一个a对象 a={ f ;function (val){}},比如没有protected 方法,这可能导致开发出来的方法被污染,还有诸如很多对象的继承限定都不是很友好。于是就有了ts的出现。
这里说明下: JS和TS的关系
TypeScript(简称 TS)和 JavaScript(简称 JS)有着密切的关系:基础与扩展:JavaScript 是一门动态、弱类型的脚本语言。TypeScript 是 JavaScript 的超集,这意味着合法的 JavaScript 代码也是合法的 TypeScript 代码。TypeScript 在 JavaScript 的基础上添加了静态类型检查和其他一些高级特性。编译目标:TypeScript 代码不能直接在浏览器或 Node.js 等环境中运行。它需要通过编译器将 TypeScript 代码编译为 JavaScript 代码,然后才能在相应的运行环境中执行。开发效率和可维护性:TypeScript 的静态类型系统可以在开发过程中提供更好的代码提示和错误检查,有助于提高开发效率,减少运行时错误,并增强代码的可维护性。
到这里我们大致明白了,由于JS的面向对象编程很不完善,所以很多库都是通过闭包函数调用的方式实现,这导致JS里面闭包是一个非常重要的概念,而在PHP/python语言中,由于类的编程强大,所以闭包使用场景就受到了极大的限制。而后来,发现闭包的缺陷也很大,包括很难适应更大规模和更复杂的开发,于是TS就出现,主要是为了编译产生JS代码,让开发效率和性能更高。这也是js设计之初,没想到要面临这么复杂的场景