您现在的位置是:网站首页 > 闭包应用场景文章详情
闭包应用场景
陈川
【
JavaScript
】
18034人已围观
8076字
闭包在事件处理中的应用
事件处理是闭包最常见的应用场景之一。当我们需要在事件回调中访问外部函数的变量时,闭包就派上用场了。考虑一个简单的计数器例子:
function setupCounter() {
let count = 0;
document.getElementById('btn').addEventListener('click', function() {
count++;
console.log(`按钮被点击了 ${count} 次`);
});
}
setupCounter();
在这个例子中,事件处理函数形成了一个闭包,它可以访问外部函数setupCounter
中的count
变量。每次点击按钮时,都会修改这个count
值,而不会与其他计数器实例冲突。
闭包在模块模式中的应用
JavaScript没有原生的模块系统(在ES6之前),闭包被广泛用于创建模块化的代码结构:
var myModule = (function() {
var privateVar = '我是私有变量';
function privateMethod() {
console.log(privateVar);
}
return {
publicMethod: function() {
privateMethod();
},
publicVar: '我可以被公开访问'
};
})();
myModule.publicMethod(); // 输出: "我是私有变量"
console.log(myModule.publicVar); // 输出: "我可以被公开访问"
console.log(myModule.privateVar); // undefined
这种模式被称为模块模式,它利用闭包创建了私有作用域,只暴露必要的接口给外部。
闭包在函数工厂中的应用
闭包可以用来创建具有特定行为的函数工厂:
function createMultiplier(factor) {
return function(x) {
return x * factor;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
每个生成的函数都"记住"了创建时传入的factor
参数,即使外部函数已经执行完毕。
闭包在数据封装和私有变量中的应用
JavaScript没有真正的私有成员概念,但闭包可以模拟这一特性:
function createPerson(name) {
let age = 0; // 私有变量
return {
getName: function() {
return name;
},
getAge: function() {
return age;
},
incrementAge: function() {
age++;
return age;
}
};
}
const person = createPerson('张三');
console.log(person.getName()); // "张三"
console.log(person.getAge()); // 0
person.incrementAge();
console.log(person.getAge()); // 1
name
和age
变量实际上对外部是不可见的,只能通过暴露的方法访问和修改。
闭包在定时器和动画中的应用
闭包在处理异步操作如定时器和动画时非常有用:
function startAnimation(duration) {
let startTime = Date.now();
const intervalId = setInterval(function() {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
console.log(`动画进度: ${(progress * 100).toFixed(1)}%`);
if (progress === 1) {
clearInterval(intervalId);
console.log('动画完成');
}
}, 16); // 约60fps
}
startAnimation(1000);
回调函数通过闭包访问startTime
和duration
,即使外部函数已经执行完毕。
闭包在记忆化(Memoization)中的应用
闭包可以用来实现记忆化,即缓存函数计算结果:
function memoize(fn) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (cache[key] !== undefined) {
console.log('从缓存中获取结果');
return cache[key];
}
console.log('计算新结果');
const result = fn.apply(this, args);
cache[key] = result;
return result;
};
}
const factorial = memoize(function(n) {
return n <= 1 ? 1 : n * factorial(n - 1);
});
console.log(factorial(5)); // 计算并缓存1-5的阶乘
console.log(factorial(5)); // 直接从缓存获取
闭包在柯里化(Currying)中的应用
闭包是实现函数柯里化的关键技术:
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
};
}
};
}
function sum(a, b, c) {
return a + b + c;
}
const curriedSum = curry(sum);
console.log(curriedSum(1)(2)(3)); // 6
console.log(curriedSum(1, 2)(3)); // 6
console.log(curriedSum(1)(2, 3)); // 6
每个返回的函数都通过闭包记住了之前传入的参数。
闭包在迭代器和生成器模拟中的应用
在ES6之前,闭包常被用来模拟迭代器行为:
function createIterator(array) {
let index = 0;
return {
next: function() {
return index < array.length ?
{ value: array[index++], done: false } :
{ done: true };
}
};
}
const iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { done: true }
闭包在状态管理中的应用
闭包可以帮助管理复杂的状态:
function createStateManager(initialState) {
let state = initialState;
const listeners = [];
return {
getState: () => state,
setState: (newState) => {
state = { ...state, ...newState };
listeners.forEach(listener => listener(state));
},
subscribe: (listener) => {
listeners.push(listener);
return () => {
const index = listeners.indexOf(listener);
if (index !== -1) listeners.splice(index, 1);
};
}
};
}
const store = createStateManager({ count: 0, user: null });
const unsubscribe = store.subscribe(state => {
console.log('状态更新:', state);
});
store.setState({ count: 1 });
store.setState({ user: { name: 'Alice' } });
unsubscribe();
闭包在防抖(Debounce)和节流(Throttle)中的应用
闭包是实现防抖和节流函数的关键:
function debounce(fn, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
function throttle(fn, interval) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= interval) {
fn.apply(this, args);
lastTime = now;
}
};
}
const debouncedResize = debounce(() => {
console.log('窗口大小改变');
}, 200);
const throttledScroll = throttle(() => {
console.log('滚动事件');
}, 100);
window.addEventListener('resize', debouncedResize);
window.addEventListener('scroll', throttledScroll);
闭包在函数组合中的应用
闭包可以实现函数的组合和管道操作:
function compose(...fns) {
return function(initialValue) {
return fns.reduceRight((value, fn) => fn(value), initialValue);
};
}
function pipe(...fns) {
return function(initialValue) {
return fns.reduce((value, fn) => fn(value), initialValue);
};
}
const add5 = x => x + 5;
const multiply3 = x => x * 3;
const square = x => x * x;
const composedFn = compose(square, multiply3, add5);
const pipedFn = pipe(add5, multiply3, square);
console.log(composedFn(2)); // ((2 + 5) * 3)^2 = 441
console.log(pipedFn(2)); // ((2 + 5) * 3)^2 = 441
闭包在延迟计算中的应用
闭包可以用于实现延迟计算或惰性求值:
function lazy(fn) {
let result;
let computed = false;
return function() {
if (!computed) {
result = fn();
computed = true;
}
return result;
};
}
const expensiveCalculation = lazy(() => {
console.log('执行复杂计算...');
let sum = 0;
for (let i = 0; i < 1000000000; i++) {
sum += i;
}
return sum;
});
console.log('准备调用');
console.log(expensiveCalculation()); // 第一次调用会执行计算
console.log(expensiveCalculation()); // 第二次调用直接返回缓存结果
闭包在异步流程控制中的应用
闭包可以帮助管理异步操作的上下文:
function asyncSequence(...fns) {
return function(callback) {
let index = 0;
function next(err, result) {
if (err) return callback(err);
const currentFn = fns[index++];
if (!currentFn) return callback(null, result);
try {
currentFn(next, result);
} catch (e) {
callback(e);
}
}
next(null, null);
};
}
const sequence = asyncSequence(
(next) => setTimeout(() => next(null, 1), 1000),
(next, prev) => setTimeout(() => next(null, prev + 2), 1000),
(next, prev) => setTimeout(() => next(null, prev * 3), 1000)
);
sequence((err, result) => {
if (err) console.error(err);
else console.log('最终结果:', result); // 9
});
闭包在DOM操作批处理中的应用
闭包可以用于优化DOM操作,实现批处理:
function createDOMBatcher() {
const updates = new Map();
let scheduled = false;
function scheduleUpdate() {
requestAnimationFrame(() => {
updates.forEach((value, element) => {
element.textContent = value;
});
updates.clear();
scheduled = false;
});
}
return function(element, value) {
updates.set(element, value);
if (!scheduled) {
scheduled = true;
scheduleUpdate();
}
};
}
const batchUpdate = createDOMBatcher();
document.querySelectorAll('.item').forEach((el, i) => {
for (let j = 0; j < 1000; j++) {
// 模拟频繁更新
setTimeout(() => batchUpdate(el, `Item ${i} - ${j}`), j * 10);
}
});