闭包
函数执形成一个不销毁的私有作用域(私有栈内存),保护里面的私有变量不受外界的干扰,这种保护机制称之为“闭包”
//=>闭包:柯理化函数
function fn() {
return function () {
}
}
var f = fn();
//=>闭包:惰性函数
var utils = (function () {
return {
}
})();
return引用类型的值(堆内存)被外界引用,栈内存不能释放
作用
1.闭包具有“保护”作用:保护私有变量不受外界的干扰
在真实项目中,尤其是团队协作开发的时候,应当尽可能的减少全局变量的使用,以防止相互之前的冲突(“全局变量污染”),那么此时我们完全可以把自己这一部分内容封装到一个闭包中,让全局变量转换为私有变量
(function () {
var n = 12;
function fn() {
}
//...
})();
不仅如此,我们封装类库插件的时候,也会把自己的程序都存放到闭包中保护起来,防止和用户的程序冲突,但是我们又需要暴露一些方法给客户使用,这样我们如何处理呢?
①JQ这种方式:把需要暴露的方法通过给WIN设置属性的方式暴露出去
(function () {
function jQuery() {
//...
}
//...
window.jQuery = window.$ = jQuery;//=>把需要供外面使用的方法,通过给WIN设置属性的方式暴露出去
})();
// jQuery();
// $();
②Zepto这种方式:基于RETURN把需要外面使用的方法暴露出去
var Zepto=(function () {
//...
return {
xxx:function () {
}
};
})();
Zepto.xxx();
2.闭包具有“保存”作用:形成不销毁的栈内存能够把一些值保存下来,方便后面的调取使用
for (var i = 0; i < tabList.length; i++) {
tabList[i].onclick = function () {
changeTab(i);
//=>执行方法,形成一个私有的栈内存,遇到变量I,I不是私有变量,向上一级作用域查找(上级作用域WINDOW)
//=>所有的事件绑定都是异步编程,绑定事件后,不需要等待执行,继续执行下一个循环任务,所以当我们点击执行方法的
时候,循环早已结束(让全局的I等于循环最后的结果3)
}
}
同步编程:一件事一件事的做,当前这件事没完成,下一个任务不能处理
异步编程:当前这件事件没有彻底完成,不在等待,继续执行下面的任务
解决方案1:自定义属性
for (var i = 0; i < tabList.length; i++) {
tabList[i].myIndex = i;
tabList[i].onclick = function () {
changeTab(this.myIndex);
//=>THIS:给当前元素的某个事件绑定方法,当事件触发,方法执行的时候,方法中的THIS是当前操作的元素对象
}
}
解决方案2:闭包
for (var i = 0; i < tabList.length; i++) {
tabList[i].onclick = (function (i) {
return function () {
changeTab(i);//=>上级作用域:自执行函数形成的作用域
}
})(i);
}
自执行函数每次执行都会形成一个栈内存,形参i为私有变量,i的值为每次循环时候全局变量的i的值,return 的函数赋值给click,栈内存不释放,每次点击时i为私有变量i的值
同理,还可这样做
for (var i = 0; i < tabList.length; i++) {
(function (n) {
tabList[n].onclick = function () {
changeTab(n);
}
})(i);
}
原理都是形成三个不销毁的私有作用域,分别存储需要的索引值
解决方案3:基于ES6解决
for (let i = 0; i < tabList.length; i++) {
tabList[i].onclick = function () {
changeTab(i);
}
}
基于ES6中的LET来创建变量,是存在块级作用域的(类似于私有作用域),初始值设置的变量是当前本次块级作用域中的变量(形成了五个块级作用域,每个块级作用域中都有一个私有变量I
作用域:(栈内存)
1.全局作用域
2.私有作用域(函数执行)
3.块级作用域(一般用大括号包起来的都是块级作用域,前提是ES6语法规范,有let)