面经三:闭包

105 阅读3分钟

1. 闭包的定义和作用

定义:闭包是一个函数和其周围的状态(词法环境)的组合。闭包让你可以从内部函数访问外部函数作用域中的变量。

作用

  • 封装私有变量:闭包可以用来隐藏内部变量,防止外部直接访问。
  • 保持状态:闭包可以捕获外部函数的变量,即使外部函数已经执行完毕。

2. 常见闭包面试题

题目1:理解闭包的输出

function outerFunc() {
    let a = 10;
    function innerFunc() {
        console.log(a);
    }
    return innerFunc;
}
let innerFunc = outerFunc();
innerFunc(); // 输出什么?

答案:输出 10innerFunc 是一个闭包,它捕获了 outerFunc 的变量 a,即使 outerFunc 已经执行完毕。

题目2:闭包与 IIFE

(function(a) {
    return (function(b) {
        console.log(a);
    })(1);
})(0);

答案:输出 0。外层函数的参数 a 被内层函数捕获,内层函数是一个闭包。

题目3:闭包与循环

for (var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(i);
    }, 0);
}

答案:输出 5, 5, 5, 5, 5。由于 var 的作用域是全局,循环结束后 i 的值为 5,所有闭包都引用了同一个变量。

改进:使用 IIFE 或 let 解决:

for (var i = 0; i < 5; i++) {
    (function(j) {
        setTimeout(function() {
            console.log(j);
        }, 0);
    })(i);
}

或者:

for (let i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(i);
    }, 0);
}

题目4:闭包与模块模式

function createCounter() {
    let count = 0;
    return function() {
        count++;
        return count;
    };
}
let counter1 = createCounter();
let counter2 = createCounter();
console.log(counter1()); // 输出什么?
console.log(counter1()); // 输出什么?
console.log(counter2()); // 输出什么?

答案:输出 1, 2, 1counter1counter2 是独立的闭包,分别捕获了独立的 count

题目5:闭包与私有变量

function Person() {
    let name = "Alice";
    this.getName = function() {
        return name;
    };
}
const person = new Person();
console.log(person.getName()); // 输出什么?
console.log(person.name); // 输出什么?

答案:输出 Aliceundefinedname 是私有变量,只能通过 getName 方法访问。

3. 面试中如何回答闭包问题

面试官可能会问:“请解释什么是闭包?”或“闭包有什么作用?”以下是一个完整的回答模板:

  • 定义:闭包是一个函数和其周围的状态(词法环境)的组合。它允许内部函数访问外部函数的变量。
  • 作用
    • 封装私有变量:通过闭包可以隐藏内部变量,防止外部直接访问。
    • 保持状态:即使外部函数执行完毕,闭包仍然可以访问其捕获的变量。
  • 示例:可以结合上述题目中的代码进行解释。

4. 面试中常见的陷阱

  • 变量提升:闭包可能会捕获变量的最终值,而不是期望的值(如循环中的变量)。
  • 内存泄漏:闭包会延长变量的生命周期,可能导致内存泄漏。
  • 异步问题:闭包在异步回调中可能会导致意外的行为。

5. 如何避免闭包中的问题

  • 使用 let 替代 varlet 的块级作用域可以避免变量提升问题。
  • 手动清理引用:如果不再需要闭包中的变量,可以手动将其设置为 null
  • 合理使用 IIFE:通过立即执行函数表达式(IIFE)创建独立的作用域。

通过这些面试题和技巧,你可以更好地理解和应用闭包,同时避免常见的陷阱。