惰性初始化模式(Lazy Initialization)的实现技巧
惰性初始化模式是一种延迟对象创建或计算的设计模式,直到真正需要时才进行初始化。这种模式特别适合资源密集型操作或需要按需加载的场景,能够有效提升性能并减少不必要的内存消耗。
基本实现原理
惰性初始化的核心思想是通过代理层控制初始化时机。在JavaScript中,通常通过以下方式实现:
- 使用标志位记录初始化状态
- 在首次访问时执行初始化
- 缓存初始化结果供后续使用
最简单的实现方式是使用闭包保存状态:
function createLazyObject(initializer) {
let cached = null;
return {
get() {
if (cached === null) {
cached = initializer();
}
return cached;
}
};
}
// 使用示例
const heavyObject = createLazyObject(() => {
console.log('执行耗时初始化');
return { data: '大型数据' };
});
console.log(heavyObject.get()); // 首次调用会初始化
console.log(heavyObject.get()); // 直接返回缓存
属性描述符实现
ES5引入的属性描述符可以更优雅地实现惰性初始化:
function lazyProperty(obj, prop, initializer) {
Object.defineProperty(obj, prop, {
configurable: true,
enumerable: true,
get() {
const value = initializer.call(this);
Object.defineProperty(this, prop, {
value,
writable: true,
configurable: true,
enumerable: true
});
return value;
}
});
}
// 使用示例
const api = {};
lazyProperty(api, 'userData', function() {
console.log('获取用户数据');
return fetch('/user-data').then(res => res.json());
});
api.userData.then(data => console.log(data)); // 首次访问触发获取
类中的惰性初始化
在ES6类中实现惰性属性有多种方式:
方式一:Getter/Setter
class HeavyCalculator {
constructor() {
this._result = null;
}
get result() {
if (this._result === null) {
console.log('执行复杂计算');
this._result = this._compute();
}
return this._result;
}
_compute() {
// 模拟耗时计算
let sum = 0;
for(let i = 0; i < 1000000; i++) {
sum += Math.sqrt(i);
}
return sum;
}
}
const calc = new HeavyCalculator();
console.log(calc.result); // 首次访问触发计算
console.log(calc.result); // 直接返回缓存
方式二:Proxy代理
function lazyInitClass(Class) {
return new Proxy(Class, {
construct(target, args) {
const instance = Reflect.construct(target, args);
return new Proxy(instance, {
get(obj, prop) {
if (prop in obj && typeof obj[prop] === 'function') {
return obj[prop].bind(obj);
}
if (prop.startsWith('_lazy_') && !(prop in obj)) {
const initProp = prop.slice(6);
if (initProp in obj && typeof obj[`_init_${initProp}`] === 'function') {
obj[prop] = obj[`_init_${initProp}`]();
}
}
return obj[prop];
}
});
}
});
}
// 使用示例
const LazyUser = lazyInitClass(class User {
_init_profile() {
console.log('加载用户资料');
return { name: '张三', age: 30 };
}
get profile() {
return this._lazy_profile;
}
});
const user = new LazyUser();
console.log(user.profile); // 首次访问触发初始化
console.log(user.profile); // 直接返回缓存
异步惰性初始化
对于需要异步加载的资源,可以使用Promise实现:
class AsyncLazyLoader {
constructor(loader) {
this._loader = loader;
this._promise = null;
this._value = null;
}
get() {
if (this._value !== null) return Promise.resolve(this._value);
if (this._promise !== null) return this._promise;
this._promise = this._loader().then(value => {
this._value = value;
return value;
});
return this._promise;
}
// 强制刷新
refresh() {
this._promise = null;
this._value = null;
return this.get();
}
}
// 使用示例
const imageLoader = new AsyncLazyLoader(() => {
return new Promise(resolve => {
setTimeout(() => {
console.log('图片加载完成');
resolve('图片数据');
}, 1000);
});
});
imageLoader.get().then(data => console.log(data)); // 首次加载
imageLoader.get().then(data => console.log(data)); // 使用缓存
应用场景与优化
图片懒加载
class LazyImage {
constructor(placeholderSrc, realSrc) {
this.img = new Image();
this.img.src = placeholderSrc;
this.realSrc = realSrc;
this.loaded = false;
// 交叉观察器实现懒加载
this.observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting && !this.loaded) {
this._loadRealImage();
this.observer.unobserve(this.img);
}
});
});
this.observer.observe(this.img);
}
_loadRealImage() {
this.loaded = true;
this.img.src = this.realSrc;
}
}
模块懒加载
结合动态import实现按需加载:
const componentLoaders = {
Chart: () => import('./components/Chart.js'),
Map: () => import('./components/Map.js'),
Calendar: () => import('./components/Calendar.js')
};
class LazyComponentLoader {
constructor() {
this._components = {};
}
async load(name) {
if (this._components[name]) {
return this._components[name];
}
if (componentLoaders[name]) {
const module = await componentLoaders[name]();
this._components[name] = module.default;
return module.default;
}
throw new Error(`未知组件: ${name}`);
}
}
// 使用示例
const loader = new LazyComponentLoader();
document.getElementById('show-chart').addEventListener('click', async () => {
const Chart = await loader.load('Chart');
new Chart().render();
});
性能考量与陷阱
-
内存泄漏风险:缓存的对象如果不及时释放可能导致内存泄漏
// 解决方案:提供清理方法 class LazyCache { constructor() { this._cache = new Map(); } get(key, initializer) { if (!this._cache.has(key)) { this._cache.set(key, initializer()); } return this._cache.get(key); } clear(key) { this._cache.delete(key); } clearAll() { this._cache.clear(); } }
-
并发初始化问题:多个地方同时触发初始化可能导致重复计算
// 使用Promise解决并发问题 function createLazyAsync(initializer) { let promise = null; return () => { if (!promise) { promise = initializer().finally(() => { // 初始化完成后允许重新加载 promise = null; }); } return promise; }; }
-
测试复杂性增加:惰性初始化可能使测试更困难
// 测试时可通过强制初始化方法解决 class TestableLazy { constructor() { this._initialized = false; } get data() { if (!this._initialized) { this._initialize(); } return this._data; } _initialize() { this._data = /* 初始化逻辑 */; this._initialized = true; } // 测试专用方法 __testOnlyInitialize() { this._initialize(); } }
高级模式:多级缓存
对于需要多层缓存的场景,可以结合WeakMap和Map实现:
class MultiLevelCache {
constructor() {
this._weakCache = new WeakMap(); // 第一级:弱引用缓存
this._strongCache = new Map(); // 第二级:强引用缓存
this._maxSize = 100; // 强引用缓存最大大小
}
get(key, initializer) {
// 尝试从弱引用缓存获取
let value = this._weakCache.get(key);
if (value !== undefined) return value;
// 尝试从强引用缓存获取
value = this._strongCache.get(key);
if (value !== undefined) {
// 提升到弱引用缓存
this._weakCache.set(key, value);
return value;
}
// 初始化并缓存
value = initializer();
this._weakCache.set(key, value);
this._strongCache.set(key, value);
// 控制强引用缓存大小
if (this._strongCache.size > this._maxSize) {
const oldestKey = this._strongCache.keys().next().value;
this._strongCache.delete(oldestKey);
}
return value;
}
}
与其它模式的结合
惰性初始化 + 工厂模式
class LazyFactory {
constructor(factoryFn) {
this._factory = factoryFn;
this._instances = new Map();
}
getInstance(key) {
if (!this._instances.has(key)) {
this._instances.set(key, {
initialized: false,
value: null,
promise: null
});
}
const record = this._instances.get(key);
if (record.initialized) {
return Promise.resolve(record.value);
}
if (record.promise) {
return record.promise;
}
record.promise = this._factory(key)
.then(value => {
record.value = value;
record.initialized = true;
return value;
});
return record.promise;
}
}
// 使用示例
const userFactory = new LazyFactory(userId =>
fetch(`/api/users/${userId}`).then(res => res.json())
);
userFactory.getInstance(123).then(user => console.log(user));
惰性初始化 + 装饰器模式
在支持装饰器的环境中:
function lazy(target, name, descriptor) {
const { get, set } = descriptor;
if (get) {
descriptor.get = function() {
const value = get.call(this);
Object.defineProperty(this, name, {
value,
enumerable: true,
configurable: true,
writable: true
});
return value;
};
}
return descriptor;
}
class DecoratorExample {
@lazy
get expensiveData() {
console.log('计算数据');
return computeExpensiveData();
}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn