大家好,我是江城开朗的豌豆,一名拥有6年以上前端开发经验的工程师。我精通HTML、CSS、JavaScript
等基础前端技术,并深入掌握Vue、React、Uniapp、Flutter
等主流框架,能够高效解决各类前端开发问题。在我的技术栈中,除了常见的前端开发技术,我还擅长3D开发,熟练使用Three.js
进行3D图形绘制,并在虚拟现实与数字孪生技术上积累了丰富的经验,特别是在虚幻引擎开发方面,有着深入的理解和实践。

我一直认为技术的不断探索和实践是进步的源泉,近年来,我深入研究大数据算法的应用与发展,尤其在数据可视化和交互体验方面,取得了显著的成果。我也注重与团队的合作,能够有效地推动项目的进展和优化开发流程。现在,我担任全栈工程师,拥有CSDN博客专家认证及阿里云专家博主称号,希望通过分享我的技术心得与经验,帮助更多人提升自己的技术水平,成为更优秀的开发者。
技术qq交流群:906392632
大家好,我是小杨,一个被JS闭包折磨了6年又爱又恨的前端工程师。今天我要带大家深入理解闭包这个让人又爱又恨的特性,分享那些年我踩过的坑和总结的优化技巧。
一、闭包是什么?一个简单的例子
javascript
function outer() {
let me = '小杨';
return function inner() {
console.log(`大家好,我是${me}`);
};
}
const sayHello = outer();
sayHello(); // "大家好,我是小杨"
看到没?inner
函数记住了outer
函数的me
变量,这就是闭包!
二、闭包的三大妙用(天使面)
1. 创建私有变量
javascript
function createCounter() {
let count = 0;
return {
increment() { count++ },
getCount() { return count }
};
}
const counter = createCounter();
counter.increment();
console.log(counter.getCount()); // 1
console.log(counter.count); // undefined
2. 实现函数柯里化
javascript
function multiply(a) {
return function(b) {
return a * b;
};
}
const double = multiply(2);
console.log(double(5)); // 10
3. 事件处理中的妙用
javascript
function setupButtons() {
for(var i = 1; i <= 3; i++) {
(function(index) {
document.getElementById(`btn-${index}`)
.addEventListener('click', function() {
console.log(`我是按钮${index}`);
});
})(i);
}
}
三、闭包的三大坑(魔鬼面)
1. 内存泄漏
javascript
function leakMemory() {
const bigData = new Array(1000000).fill('*');
return function() {
console.log('我还记得bigData');
};
}
const leaked = leakMemory();
// bigData本应该被回收,但闭包让它一直存在
2. 性能问题
javascript
function slowPerformance() {
const data = {}; // 大对象
return function(key, value) {
data[key] = value;
// 每次调用都要访问闭包变量
};
}
3. 意外的变量共享
javascript
function createFunctions() {
let funcs = [];
for(var i = 0; i < 3; i++) {
funcs.push(function() {
console.log(i); // 全是3!
});
}
return funcs;
}
四、闭包优化六大法则(6年经验总结)
1. 及时释放引用
javascript
function createHeavyObject() {
const heavy = new Array(1000000).fill('*');
return {
useHeavy: function() {
// 使用heavy
},
cleanup: function() {
heavy = null; // 手动释放
}
};
}
2. 使用块级作用域
javascript
// 修复前面的共享变量问题
function createFixedFunctions() {
let funcs = [];
for(let i = 0; i < 3; i++) { // 使用let
funcs.push(function() {
console.log(i); // 0,1,2
});
}
return funcs;
}
3. 避免不必要的闭包
javascript
// 不好的写法
function unneededClosure() {
const data = {};
return function() {
// 根本不使用data,却形成了闭包
console.log('Hello');
};
}
// 好的写法
function noClosure() {
console.log('Hello');
}
4. 使用WeakMap管理私有变量
javascript
const privateData = new WeakMap();
class MyClass {
constructor() {
privateData.set(this, {
secret: '我是私有数据'
});
}
getSecret() {
return privateData.get(this).secret;
}
}
5. 合理使用IIFE
javascript
// 立即执行函数减少闭包生命周期
(function() {
const tempData = processData();
// 使用tempData
})(); // 执行完立即释放
6. 使用模块化
javascript
// 模块化天然适合管理闭包
const counterModule = (function() {
let count = 0;
return {
increment() { count++ },
getCount() { return count }
};
})();
五、真实案例分享
案例1:我曾经在项目中遇到一个页面卡顿问题,最后发现是因为一个事件处理函数形成了闭包,持有了一个大DOM树的引用。解决方案是:
javascript
// 修复前
function setup() {
const bigElement = document.getElementById('big');
button.addEventListener('click', function() {
// 持有bigElement引用
console.log(bigElement.id);
});
}
// 修复后
function setup() {
const id = 'big';
button.addEventListener('click', function() {
// 只存储需要的id
console.log(id);
});
}
六、总结
闭包就像一把双刃剑:
✅ 优点:实现私有变量、函数柯里化、模块化等
❌ 缺点:可能导致内存泄漏、性能问题
记住我的6年经验总结:
- 及时释放不再需要的引用
- 优先使用块级作用域
- 避免不必要的闭包
- 合理使用WeakMap和模块化
- 善用开发者工具检查内存
最后留个思考题:
javascript
function createFunctions() {
let funcs = [];
for(var i = 0; i < 3; i++) {
funcs.push(function(j) {
return function() {
console.log(j);
};
}(i));
}
return funcs;
}
const funcs = createFunctions();
funcs[0](); // 输出什么?为什么?
欢迎在评论区讨论你的答案!下期我会分享更多JS高级技巧。