JS闭包是指函数可以访问当前环境外的变量,并在执行后保留对这些变量的引用。通俗的说,就是函数内部的函数可以访问函数外部函数的变量。下面我们来一步一步详细讲解JS闭包所用的场合以及优缺点分析。
什么是闭包
在JS中,每当创建一个函数,该函数就会创建一个作用域(scope)链。作用域链可以帮助函数在查找变量时,逐级向上进行查找,直到找到为止。而闭包,正是通过这个作用域链来实现对父级作用域中变量的访问。
闭包的用处
封装变量
JS闭包可以避免全局变量带来的命名冲突,可以将一个变量封装在一个函数内部,从而防止其他函数修改该变量。如下所示:
function createCounter() {
var count = 0;
return function () {
count++;
console.log(count);
}
}
var counter = createCounter();
counter(); // 输出1
counter(); // 输出2
counter(); // 输出3
在上述代码中,我们首先定义了一个外部函数createCounter,并在其中定义了一个局部变量count。随后我们又定义了一个内部函数,并在内部函数中修改了count变量的值。最后将内部函数作为函数createCounter的返回值返回。
我们可以发现,在createCounter函数执行完之后,count变量并没有从内存中销毁,而是在内部函数中得以访问。这就是闭包的内存保留机制。
实现函数或对象私有化
闭包能够让我们实现一些私有方法的效果,因为闭包可以使得函数内部的变量在函数执行完毕后仍然存活在内存中,不会被垃圾回收站收回。这就可以用来实现JS中的对象私有化。如下所示:
function Point(x, y) {
this.x = x;
this.y = y;
var self = this;
var length = 0; //私有变量
// 计算点到原点的距离
var calcLength = function () {
length = Math.sqrt(self.x * self.x + self.y * self.y);
}
calcLength();
this.getLength = function () {
return length;
}
}
var point = new Point(3, 4);
console.log(point.getLength()); // 输出 5
console.log(point.length); // 输出 undefined
我们在Point函数中定义了一个私有变量length,用于保存点到原点的距离。在函数中还定义了一个私有函数calcLength用于计算长度。由于length被定义在Point函数的内部,所以我们不能直接访问到length变量。但是在Point函数中又定义了一个公有方法getLength,该方法能够返回length的值。
通过上述代码,我们可以看到,point对象虽然不能直接访问到length,但是却能够通过调用getLength方法来获取到length的值。
闭包的缺点
尽管闭包可以为我们提供很多便利,但是它也有一些缺点。
使用闭包需要注意内存泄漏问题。当函数执行完毕,内部变量仍然被占用,不会被回收,这就造成了内存泄漏。为了避免内存泄漏,我们需要注意不要在闭包中存储过多的外部变量。
闭包中的作用域链访问虽说便利,但是它对性能也有一定的影响。因为作用域链的查找步骤需要多次访问内存,效率比较低下,因此过度使用闭包会导致页面性能下降,甚至造成浏览器的卡顿。
闭包的示例
示例1:封装变量
var counter = (function() {
var count = 0;
return function() {
count++;
console.log(count);
};
})(); // 每次调用都会自增1
counter(); // 输出1
counter(); // 输出2
counter(); // 输出3
示例2:使用闭包实现节流函数
function throttle(fn, interval) {
var startTime = 0;
return function() {
var endTime = new Date().getTime();
if(endTime - startTime > interval) {
fn.apply(this, arguments);
startTime = endTime;
}
};
}
var func = function() {
console.log('hello world');
};
var throtFunc = throttle(func, 1000);
throtFunc();
setTimeout(throtFunc, 500);
setTimeout(throtFunc, 900);
setTimeout(throtFunc, 1200);
setTimeout(throtFunc, 1600);
在上述示例中,我们定义了一个函数throttle,函数接收两个参数,第一个参数是要包装的函数,第二个参数表示间隔多少毫秒执行一次。throttle函数内部又返回了一个匿名函数,并在该匿名函数内部实现了函数节流的功能。最后,我们可以通过throttle函数来实现对其他函数的节流处理。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:js闭包所用的场合以及优缺点分析 - Python技术站