基本使用
闭包是JavaScript中的一个重要概念,它允许函数访问并操作函数外部的变量。下面通过两个典型示例展示闭包的基本用法。
基础闭包示例:
function outFunc() {
const name = "王独秀";
function inFunc() {
console.log(name);
}
return inFunc;
}
const inFunc = outFunc();
inFunc(); // 输出:王独秀
模拟私有属性:
function User() {
this.name = "王独秀"; // 公有属性
const age = 28; // 私有属性
this.sayAge = function() {
console.log("my age is " + age);
}
}
const user = new User();
console.log(user.name); // 王独秀(公有属性可访问)
console.log(user.age); // undefined(私有属性不可直接访问)
user.sayAge(); // my age is 28(通过闭包访问私有属性)
应用场景
1. 返回值(最常用)
function fn(){
var name = "Symbol(王独秀)";
return function(){
return name;
}
}
var fnc = fn();
console.log(fnc()); // Symbol(王独秀)
2. 函数赋值
var fn2;
function fn(){
var name = "Symbol(王独秀)";
// 将闭包函数赋值给外部变量
fn2 = function(){
return name;
}
}
fn(); // 先执行函数完成赋值
console.log(fn2()); // Symbol(王独秀)
3. 函数参数
function fn(){
var name = "王独秀";
return function callback(){
return name;
}
}
var fn1 = fn(); // 获取闭包函数
function fn2(f){
// 将闭包函数作为参数传入
console.log(f());
}
fn2(fn1); // 输出:王独秀
4. IIFE(自执行函数)
(function(){
var name = "王独秀";
var fn1 = function(){
return name;
}
// 在自执行函数中直接调用外部函数
fn2(fn1);
})();
function fn2(f){
console.log(f()); // 输出:王独秀
}
5. 循环赋值
// 每秒输出1次,分别显示1-10
for(var i = 1; i <= 10; i++){
(function(j){
// 使用闭包保存当前循环的值
setTimeout(function(){
console.log(j);
}, j * 1000);
})(i); // 立即传入当前i值
}
6. Getter和Setter
function fn(){
var name = '王独秀';
setName = function(n){
name = n;
}
getName = function(){
return name;
}
return {
setName: setName,
getName: getName
}
}
var fn1 = fn();
console.log(fn1.getName()); // getter
fn1.setName('world'); // setter修改闭包内的name
console.log(fn1.getName()); // getter
7. 迭代器
var arr = ['aa', 'bb', 'cc'];
function incre(arr){
var i = 0;
return function(){
return arr[i++] || '数组值已经遍历完';
}
}
var next = incre(arr);
console.log(next()); // aa
console.log(next()); // bb
console.log(next()); // cc
console.log(next()); // 数组值已经遍历完
8. 首次区分(避免重复执行)
var fn = (function(){
var arr = []; // 缓存数组
return function(val){
if(arr.indexOf(val) == -1){
arr.push(val);
console.log('函数被执行了', arr);
// 实际业务逻辑
} else {
console.log('此次函数不需要执行');
}
console.log('已缓存的数组:', arr);
}
})();
fn(10); // 函数被执行了 [10]
fn(10); // 此次函数不需要执行
fn(1000); // 函数被执行了 [10,1000]
9. 缓存优化
var fn = (function(){
var cache = {}; // 缓存对象
var calc = function(arr){
var sum = 0;
for(var i = 0; i < arr.length; i++){
sum += arr[i];
}
return sum;
}
return function(){
var args = Array.prototype.slice.call(arguments, 0);
var key = args.join(",");
var result, tSum = cache[key];
if(tSum){
console.log('从缓存中取:', cache);
result = tSum;
} else {
result = cache[key] = calc(args);
console.log('存入缓存:', cache);
}
return result;
}
})();
fn(1,2,3,4,5); // 存入缓存
fn(1,2,3,4,5); // 从缓存中取
fn(1,2,3,4,5,6); // 存入新缓存
10. 事件处理中的索引获取
<button>button1</button>
<button>button2</button>
<button>button3</button>
<script>
const btns = document.querySelectorAll('button');
// 使用闭包方案
for (let i = 0; i < btns.length; i++) {
btns[i].onclick = (function(tmpI) {
return function() {
console.log(tmpI + 1);
}
})(i)
}
// 或者使用let声明(现代[前端开发](https://yunpan.plus/f/22-1)推荐)
for (let i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
console.log(i + 1);
}
}
</script>
闭包在函数式编程中扮演着重要角色,合理运用可以显著提升代码的可维护性和性能表现。通过上述10个实际场景的演示,相信大家对闭包的理解会更加深入。