作为前端开发,闭包是时时刻刻都在使用的,理解闭包是十分重要的,下面从闭包的定义,使用场景,及优缺点进行总结,帮助大家更好的理解闭包。
引用自 MDN关于闭包的描述
闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。
闭包 = 函数 + 函数内部能访问的所有变量
换句话说:闭包是指在函数内部定义的函数可以访问到外部函数的变量,即使外部函数已经执行完毕退出了,内部函数仍然可以访问到外部函数的变量。如下
function outerFunction() {
const outerVariable = "Hello, world!";
function innerFunction() {
console.log(outerVariable);
}
return innerFunction;
}
const closure = outerFunction();
closure(); // 输出 "Hello, world!"
// count变量只能在createCounter内部使用
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
}
}
function delay(func, time) {
return function() {
setTimeout(func, time);
}
}
const delayedFunc = delay(function() {
console.log('Hi!');
}, 1000);
delayedFunc(); // 1 秒后输出 "Hi!"
// delay 函数返回一个内部函数,该函数在被调用时才会执行 func 函数,
// 并且在 time 毫秒后才会执行,达到了延迟执行的目的
常说的防抖节流中
// 防抖
function debounce(fn,delay) {
let timer = null
return function() {
let args = arguments
if(timer) {
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.apply(this,args)
},delay)
}
}
// debounce 函数返回一个内部函数,该函数在被连续调用时会清除之前的定时器,
// 延迟一段时间后再执行 func 函数。
// 由于需要在延迟执行的过程中访问外部变量 timer,因此使用了闭包
// 节流
function throttle(fn,delay) {
let record = Date.now()
return function(...args) {
let now = Date.now()
const interval = now - record
if(interval>delay) {
fn(...args)
record = now
}
}
}
// 由于需要在内部函数中保存上一次执行的时间record,因此使用了闭包。
function memoize(func) {
const cache = {};
return function(arg) {
if (arg in cache) {
return cache[arg];
} else {
const result = func(arg);
cache[arg] = result;
return result;
}
}
}
function expensiveCalculation(n) {
console.log('Calculating...');
return n * 2;
}
const memoizedCalculation = memoize(expensiveCalculation);
memoizedCalculation(5); // 输出 "Calculating..." 和 10
memoizedCalculation(5); // 直接输出 10,不再计算
const module = (function() {
let privateVar = 0;
function privateFunc() {
console.log('Private function');
}
return {
publicVar: 1,
publicFunc: function() {
console.log('Public function');
}
}
})();
console.log(module.publicVar); // 输出 1
module.publicFunc(); // 输出 "Public function"
console.log(module.privateVar); // 输出 undefined
module.privateFunc(); // 报错,因为无法访问私有函数
// 使用立即执行函数和闭包的方式实现了模块化开发,将私有变量和函数封装在了内部,
// 对外暴露了公共接口,提高了代码的可维护性
function fetchData(url, callback) {
fetch(url)
.then(response => response.json())
.then(data => callback(data))
.catch(error => console.error(error));
}
function displayData(data) {
console.log(data);
}
fetchData('https://jsonplaceholder.typicode.com/todos/1', displayData);
// 将函数作为参数传递给另一个函数,并在需要的时候调用
一句话:私有化数据,并且在私有化数据的基础上可以保存数据
内存泄漏
闭包会导致一些变量一直被引用,而无法被垃圾回收器回收,从而导致内存泄漏。一般来说,在使用闭包时,要确保不再需要某个变量时,要将其设置为null,以便垃圾回收器回收。
性能问题
由于闭包会在函数执行完毕后继续占用内存,因此使用闭包会对性能造成一定的影响。在需要频繁调用的函数中,使用闭包可能会导致函数执行速度变慢。
综上,我们在使用闭包的时候,需要注意避免闭包中的变量一直被引用,可以在不需要使用闭包的时候,将闭包设置为null,以便垃圾回收器回收,避免内存占用过高的问题。
垃圾回收机制是一种自动化的内存管理技术,用于检测和回收不再被程序使用的内存。在 JavaScript 中,垃圾回收机制是由 JavaScript 引擎自动执行的,程序员无需手动管理内存。
垃圾回收机制的基本原理是标记清除和引用计数。
在 JavaScript 中,垃圾回收机制的具体实现由 JavaScript 引擎负责。不同的JavaScript 引擎有不同的垃圾回收机制,例如 V8 引擎采用了分代垃圾回收机制,将内存分为新生代和老生代两个区域,并对它们采用不同的垃圾回收策略。
总之,垃圾回收机制是一种自动化的内存管理技术,可以帮助程序员避免内存泄漏和内存溢出等问题,提高代码的可靠性和性能。