您现在的位置是:网站首页 > 垃圾回收机制对设计模式的影响文章详情

垃圾回收机制对设计模式的影响

垃圾回收机制的基本原理

JavaScript的垃圾回收机制主要基于引用计数和标记清除两种算法。引用计数通过跟踪每个对象的引用次数来判断是否回收,而现代浏览器普遍采用的标记清除算法则会从根对象出发,标记所有可达对象,然后清除未被标记的对象。

// 引用计数示例
let obj1 = { name: 'obj1' }; // 引用计数: 1
let obj2 = obj1;             // 引用计数: 2
obj1 = null;                 // 引用计数: 1
obj2 = null;                 // 引用计数: 0 (可回收)

单例模式的内存管理

单例模式需要特别注意内存泄漏问题。由于单例对象会长期存在于内存中,如果其中包含对大对象的引用,即使这些对象不再需要也不会被回收。

class Singleton {
  constructor() {
    if (!Singleton.instance) {
      Singleton.instance = this;
      this.cache = new Map(); // 可能造成内存泄漏
    }
    return Singleton.instance;
  }
  
  // 应该提供清理方法
  clearCache() {
    this.cache.clear();
  }
}

观察者模式中的引用关系

观察者模式中常见的错误是忘记移除观察者,导致观察者对象无法被回收。特别是在DOM事件监听中,这种现象尤为常见。

class Subject {
  constructor() {
    this.observers = [];
  }
  
  addObserver(observer) {
    this.observers.push(observer);
  }
  
  // 必须提供移除方法
  removeObserver(observer) {
    const index = this.observers.indexOf(observer);
    if (index > -1) {
      this.observers.splice(index, 1);
    }
  }
}

工厂模式与对象池技术

垃圾回收机制促使我们在频繁创建销毁对象时考虑对象池技术。工厂模式可以很好地与对象池结合,减少GC压力。

class ObjectPool {
  constructor(createFn) {
    this.createFn = createFn;
    this.pool = [];
  }
  
  acquire() {
    return this.pool.length > 0 ? this.pool.pop() : this.createFn();
  }
  
  release(obj) {
    // 重置对象状态
    this.pool.push(obj);
  }
}

// 使用示例
const pool = new ObjectPool(() => ({ x: 0, y: 0 }));
const obj = pool.acquire();
// 使用完毕后
pool.release(obj);

装饰器模式与内存泄漏

装饰器模式容易在装饰链中形成长引用链,如果不注意断开引用,可能导致原始对象无法被回收。

function withLogging(fn) {
  return function(...args) {
    console.log('Calling function');
    return fn.apply(this, args);
  };
}

// 使用后应该保留原始引用以便取消装饰
const originalFn = someObject.method;
someObject.method = withLogging(originalFn);

// 取消装饰
someObject.method = originalFn;

策略模式中的函数对象

策略模式中频繁创建函数对象可能增加GC负担,可以考虑缓存策略函数或使用原型共享。

const strategies = {
  add: (a, b) => a + b,
  subtract: (a, b) => a - b
  // 避免在每次调用时创建新函数
};

function calculate(strategy, a, b) {
  return strategies[strategy](a, b);
}

代理模式与WeakMap

Proxy对象可以拦截各种操作,但要注意代理目标的生命周期。WeakMap可以帮助管理这种关系而不妨碍垃圾回收。

const proxyCache = new WeakMap();

function createProxy(target) {
  if (proxyCache.has(target)) {
    return proxyCache.get(target);
  }
  
  const proxy = new Proxy(target, {
    get(target, prop) {
      console.log(`Getting ${prop}`);
      return target[prop];
    }
  });
  
  proxyCache.set(target, proxy);
  return proxy;
}

命令模式与撤销操作

命令对象通常需要保存状态以实现撤销,这可能导致内存占用增加。可以通过限制历史记录长度或定期清理来解决。

class CommandManager {
  constructor(maxHistory = 50) {
    this.history = [];
    this.maxHistory = maxHistory;
  }
  
  execute(command) {
    command.execute();
    this.history.push(command);
    
    // 防止内存无限增长
    if (this.history.length > this.maxHistory) {
      this.history.shift();
    }
  }
}

享元模式的内在状态

享元模式通过共享相同内在状态来减少内存使用,这与垃圾回收机制的目标高度一致。

class FlyweightFactory {
  constructor() {
    this.flyweights = {};
  }
  
  getFlyweight(key) {
    if (!this.flyweights[key]) {
      this.flyweights[key] = new Flyweight(key);
    }
    return this.flyweights[key];
  }
}

class Flyweight {
  constructor(intrinsicState) {
    this.intrinsicState = intrinsicState;
  }
  
  operation(extrinsicState) {
    console.log(`Intrinsic: ${this.intrinsicState}, Extrinsic: ${extrinsicState}`);
  }
}

职责链模式中的中间件

在Node.js中间件系统中,职责链模式被广泛使用。需要注意中间件数组的增长可能导致内存问题。

class Middleware {
  constructor() {
    this.middlewares = [];
  }
  
  use(fn) {
    this.middlewares.push(fn);
  }
  
  // 应该提供移除中间件的方法
  remove(fn) {
    const index = this.middlewares.indexOf(fn);
    if (index > -1) {
      this.middlewares.splice(index, 1);
    }
  }
}

访问者模式与对象遍历

访问者模式在遍历复杂对象结构时,需要注意避免在访问过程中创建大量临时对象。

class Visitor {
  visitElement(element) {
    // 避免在visit方法中创建新对象
    element.process();
  }
}

// 使用对象池优化访问者实例
const visitorPool = new ObjectPool(() => new Visitor());
const visitor = visitorPool.acquire();
// 使用后归还
visitorPool.release(visitor);

状态模式中的状态转换

状态模式中状态对象的频繁切换可能增加GC压力,可以通过共享状态对象来优化。

class State {
  constructor() {
    if (State._instance) {
      return State._instance;
    }
    State._instance = this;
  }
}

// 共享状态实例
const state1 = new State();
const state2 = new State();
console.log(state1 === state2); // true

备忘录模式与内存消耗

备忘录模式保存对象状态可能占用大量内存,应该考虑限制快照数量或使用增量存储。

class Originator {
  constructor() {
    this.state = {};
  }
  
  save() {
    return new Memento({ ...this.state });
  }
  
  restore(memento) {
    this.state = { ...memento.getState() };
  }
}

class Memento {
  constructor(state) {
    this.state = state;
  }
  
  getState() {
    return this.state;
  }
}

中介者模式与对象引用

中介者作为通信中心,持有大量组件引用,需要特别注意在组件销毁时从中介者中注销。

class Mediator {
  constructor() {
    this.components = new Set();
  }
  
  register(component) {
    this.components.add(component);
    component.setMediator(this);
  }
  
  // 必须提供注销方法
  unregister(component) {
    this.components.delete(component);
    component.setMediator(null);
  }
}

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

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