闭包
什么是闭包
- 函数执行形成一个私有的作用域,保护里面的私有变量不受外界的干扰,这种保护机制称之为“闭包”
- 开发者普遍认为的闭包是:形成一个不销毁的私有作用域(私有栈内存)才是闭包
闭包的2种写法
闭包:柯理化函数
function fn(){
return function(){};
}
var f = fn();
闭包:惰性函数
var utils = (function(){
return {};
})();
闭包的实际应用
真实项目中为了保证JS的性能(堆栈内存的性能优化),应该尽可能的减少闭包的使用(不销毁的堆栈内存是耗性能的)
- 闭包具有保护作用:保护私有变量不受外界的干扰
在真实项目中,尤其是团队协作开发的时候,应当尽可能的减少全局变量的使用,以防止相互之间的冲突(“全局变量污染”),那么此时我们完全可以把自己这一部分内容封装到一个闭包中,让全局变量转换为私有变量。
(function(){
var n = 12;
function fn(){
}
})();
不仅如此,封装类插件的时候,也会把程序都存放到闭包中保护起来,防止和用户的程序冲突,但是又需要暴露一些方法给客户使用,这样我们如何处理?
- JQ这种方法:把需要暴露的方法抛到全局
(function (){
function jQuery() {
console.log(1);
}
window.jQuery = window.$ = jQuery;
})();
jQuery();
$();
- Zepto这种方式:基于RETURN把需要共外面使用的方法暴露出去
var Zepto = (function (){
return {
xxx: function (){
console.log(1);}
};
})();
Zepto.xxx();
- 闭包具有保存作用:形成不销毁的栈内存,把一些值保存下来,方便后面的调取使用
function changeTab(n) {
console.log(n);
}
let tabList = [{}, {}, {}];
for (var i = 0; i < tabList.length; i++) {
tabList[i].onclick = (function (i) {
// 让自执行函数执行,把执行的返回值(return)赋值给ON-CLICK(此处ON-CLICK绑定的是返回的小函数,
// 点击的时候执行的是小函数),自执行函数在给事件赋值的时候就已经执行了
return function () {
changeTab(++i); // 上级作用域:自执行函数形成的作用域
};
})(i);
}
for (var i = 0; i < tabList.length; i++) {
console.log(tabList[i]);
tabList[i].onclick();
}
/**
* 循环三次,形成3个不销毁的私有作用域(自执行函数执行),而每一个不销毁的栈内存中都存储了一个私有变量,而这个值分别是每一次执行传递进来的全局i的值(也就是:第一个不销毁的作用域存储的是0,第二个是1,第三个是2):
* 当触发点击事件,执行返回的小函数,遇到变量I,向它自己的上级作用域查找,找到的I值分别是:0/1/2,达到了我们想要的效果
*/
for (var i = 0; i < tabList.length; i++) {
console.log(tabList[i]);
tabList[i].onclick();
}
这样非常损耗性能
其他写法,相同效果
for (var i = 0; i < tabList.length; i++) {
(function (i) {
tabList[i].onclick = function () {
changeTab(++i);
};
})(i);
}
基于ES6中的LET来创建变量,是存在块级作用域的(类似于私有作用域)
for (let i = 0; i < tabList.length; i++) {
tabList[i].onclick = function () {
changeTab(++i);
};
}
判断体、循环体也都是块级作用域,初始值设置的变量是当前本次块级作用域中的变量,循环几次就有几个块级作用域
{
let a = 5;
console.log(a);
}
console.log(a); // a is not defined