您现在的位置是:网站首页 > 闭包应用场景文章详情

闭包应用场景

闭包在事件处理中的应用

事件处理是闭包最常见的应用场景之一。当我们需要在事件回调中访问外部函数的变量时,闭包就派上用场了。考虑一个简单的计数器例子:

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

nameage变量实际上对外部是不可见的,只能通过暴露的方法访问和修改。

闭包在定时器和动画中的应用

闭包在处理异步操作如定时器和动画时非常有用:

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);

回调函数通过闭包访问startTimeduration,即使外部函数已经执行完毕。

闭包在记忆化(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);
  }
});

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步