MST

星途 面试题库

面试题:JavaScript 数据类型与变量作用域的深度分析

在 JavaScript 中,闭包是如何依赖于变量声明和数据类型的?举例说明闭包如何访问和修改外部函数的变量,并且阐述闭包中数据类型可能引发的内存问题。如果在一个立即执行函数表达式(IIFE)内部使用 let 声明变量,该变量的作用域是怎样的,与使用 var 声明有何不同?
14.7万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

闭包与变量声明和数据类型的关系

  1. 变量声明:闭包可以访问外部函数作用域中的变量,即使外部函数已经执行完毕。外部函数的变量在闭包创建时被“捕获”,只要闭包存在,这些变量就不会被垃圾回收机制回收。例如:
function outer() {
    let localVar = 10;
    return function inner() {
        console.log(localVar);
    };
}
let closure = outer();
closure(); // 输出 10

在上述代码中,inner 函数形成了闭包,它可以访问 outer 函数中的 localVar 变量,尽管 outer 函数已经执行结束。 2. 数据类型:闭包中涉及的数据类型对其行为和内存管理有影响。基本数据类型(如 numberstringboolean 等)在闭包中按值传递和存储。而引用数据类型(如 objectarray)在闭包中按引用传递。这意味着,如果闭包修改了引用数据类型的值,外部函数作用域中的该引用数据类型也会被修改。例如:

function outer() {
    let obj = { value: 20 };
    return function inner() {
        obj.value++;
        console.log(obj.value);
    };
}
let closure = outer();
closure(); // 输出 21

这里 obj 是引用数据类型,闭包 innerobj 的修改会反映到外部函数 outer 作用域中的 obj

闭包访问和修改外部函数变量

闭包通过在内部函数中直接访问外部函数作用域中的变量来实现访问和修改。例如:

function counter() {
    let count = 0;
    return function increment() {
        count++;
        return count;
    };
}
let myCounter = counter();
console.log(myCounter()); // 输出 1
console.log(myCounter()); // 输出 2

counter 函数中,increment 函数形成闭包,它可以访问并修改 count 变量。每次调用 increment 时,count 都会自增。

闭包中数据类型引发的内存问题

当闭包引用了较大的对象或数组等引用数据类型时,由于闭包会阻止这些数据被垃圾回收机制回收,如果闭包长时间存在,可能会导致内存泄漏。例如:

function outer() {
    let largeArray = new Array(1000000).fill(1);
    return function inner() {
        return largeArray.length;
    };
}
let closure = outer();
// 这里即使 outer 函数执行完毕,largeArray 由于被闭包引用,不会被回收,占用大量内存

如果 closure 长时间存在且不释放,largeArray 占用的内存就无法被回收,导致内存泄漏。

IIFE 内部 let 与 var 声明变量的作用域

  1. let 声明:在 IIFE 内部使用 let 声明的变量,其作用域仅限于该 IIFE 块。例如:
(function () {
    let localVar = 'let 变量';
    console.log(localVar); // 输出 'let 变量'
})();
console.log(localVar); // 报错,localVar 未定义

localVar 仅在 IIFE 内部有效,外部无法访问。 2. var 声明:使用 var 声明的变量,其作用域是整个函数作用域,而不是块级作用域。例如:

(function () {
    var localVar = 'var 变量';
    if (true) {
        var localVar = '新的 var 变量';
        console.log(localVar); // 输出 '新的 var 变量'
    }
    console.log(localVar); // 输出 '新的 var 变量'
})();

在这个 IIFE 中,var 声明的 localVar 作用域是整个函数体,在 if 块中重新声明 localVar 实际上是对同一个变量的重新赋值。而 let 声明在块级作用域内是独立的,不同块中的 let 变量可以同名且相互独立。