# JavaScript 闭包
# 闭包
闭包可以让一个函数访问并操作其声明时的作用域中的变量和函数,并且,即使声明时的作用域消失了,也可以调用
复杂的解释:
闭包就是由函数创造的一个词法作用域,里面创建的变量被引用后,可以在这个词法环境之外自由使用。 闭包通常用来创建内部变量,使得这些变量不能被外部随意修改,同时又可以通过指定的函数接口来操作。
最简单的闭包
// 全局作用域就是一个闭包
var outerVal = 'lionel';
function outerFn(){
console.log(outerVal)
}
outerFn() // lionel
私有变量
闭包常见的用法,封装私有变量。用户无法直接获取和修改变量的值,必须通过调用方法;并且这个用法可以创建只读的私有变量
function People(num) { // 构造器
var age = num;
this.getAge = function() {
return age;
};
this.addAge = function() {
age++;
};
console.log(this)
}
绑定函数上下文
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error');
}
var _this = this;
var args = [...arguments].slice(1);
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以 new F(),所以需要判断
if (this instanceof F) {
return new _this(...args, ...arguments);
}
return _this.apply(context, args.concat(...arguments));
}
}
缓存记忆 利用闭包来实现一个memoize函数
# 循环和闭包
for(var i = 0; i < 5; i++) {
setTimeout(function timer() {
console.log(i);
}, 1000);
}
//输出: 一秒后,连续输出5个5
for(var i = 0; i < 5; i++) {
(function() {
setTimeout(function timer() {
console.log(i);
}, 1000);
})();
}
//输出: 一秒后,连续输出5个5
for(var i = 0; i < 5; i++) {
(function(j) {
setTimeout(function timer() {
console.log(j);
}, 1000);
})(i);
}
//输出: 一秒后,连续输出0,1,2,3,4
for(let i = 0; i < 5; i++) {
setTimeout(function timer() {
console.log(i);
}, 1000);
}
//使用了let声明
//输出: 一秒后,连续输出0,1,2,3,4
for(var i = 0; i < 5; i++) {
(function(j) {
setTimeout(function timer() {
console.log(j);
}, 1000 * j);
})(i);
}
//输出:每隔一秒,输出0,1,2,3,4
# 模块和闭包
function CoolModule() {
let something = "cool";
let another = [1, 2, 3];
function doSomething() {
console.log(something);
}
function doAnother() {
console.log(another.join("!"));
}
return {
doSomething: doSomething,
doAnother: doAnother
};
}
var foo = CoolModule();
foo.doSomething();//cool
foo.doAnother();//1!2!3
# 使用闭包注意
1.由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除
# 应用
- 私有变量
- 回调与计时器
- 绑定函数上下文
- 偏应用函数
- 函数重载:缓存记忆、函数包装
- 即时函数:独立作用域、简洁代码、循环、类库包装、通过参数限制作用域内的名称