JS 经典闭包面试题

1.带参闭包问题

function foo(x) {
    var tmp = 3;
    return function f2(y) {
        alert(x + y + (++tmp));
    };
}
var bar = foo(3); // bar 现在是一个闭包
bar(10);

:首先执行var bar = foo(3);那么foo就执行了,参数3也传进去了,但是执行完毕后,tmp变量以及参数x就已经被释放回收了吗?
并没有,因为返回值里面还等待使用这些变量,所以此时,foo虽然执行了,但是foo的变量并没有被释放,在return中等待继续使用这些变量,这时bar就是一个闭包。

2.事件闭包问题
例:很多元素绑定一个点击事件
点击每个按钮时拿到当前按钮所对应的索引值
CSS样式如下:

<button class="btn">按钮</button>
<button class="btn">按钮</button>
<button class="btn">按钮</button>
<button class="btn">按钮</button>
<button class="btn">按钮</button>
<button class="btn">按钮</button>
<button class="btn">按钮</button>

首先看拿不到在for循环里当前对象的i值的写法:

var btn = document.getElementsByClassName("btn");
for (var i = 0; i < btn.length; i++) {
    btn[i].onclick = function () {
        alert(i);
    }
}

注:网页初始化时,每个按钮的事件都已经绑好了,用户触发都是后续触发的,后续触发说明事件有了,事件有了说明for循环执行完成,i就已经拿到最大值
问题:怎样在for循环里拿到当前对象的i值
解决:索引对索引
写法一:参数的传递

var btn = document.getElementsByClassName("btn");
for (var i = 0; i < btn.length; i++) {
    //参数的传递  桥梁
    btn[i].index = i;
    btn[i].onclick = function () {
        alert(this.index);
    }
}

写法二:自执行函数的闭包 自执行函数的参是后面跟括号里的参数传的

var btn = document.getElementsByClassName("btn");
for (var i = 0; i < btn.length; i++) {
    (function (x) {
        btn[i].onclick = function () {
            alert(x);
        }
    })(i);  //这个i是for循环的i
}

:for循环每一次都执行一个自执行函数,每一次变量i被当做参数传到IIEF(自执行函数)中去,那么这个IIEF中创建了一个变量参数x,然后元素节点btn绑定了一个onclick事件,执行函数里面需要用到这个参数x,但是你又没点,那么这个变量x就没有被清理,就一直在参数里面被保存着,每一个IIEF都做一样的事情,所以这个时候就产生了闭包,变量x并没有被回收,依然在等待使用。

3.面向对象

function fun(n, o) {
    console.log(o)
    return {
        fun: function (m) {
            return fun(m, n);
        }
    };
}
var a = fun(0);
a.fun(1);
a.fun(2);
a.fun(3);
var b = fun(0).fun(1).fun(2).fun(3);
var c = fun(0).fun(1);
c.fun(2);
c.fun(3);

4.首先解释arguments
arguments指当前函数的参数列表,类似于数组的集合,使用索引值取;
arguments.length返回当前参数列表的长度

function f1(a, b, c) {
    console.log(arguments[0]);  //4
    console.log(arguments.length);  //3
}
f1(4, 5, 6);

例:

function f1(a, b, c) {
    /*arguments 是当前函数的参数列表  类似于数组的集合  使用索引值来取*/
    console.log(arguments.length);
}
f1(1, 2, 3);
var sum = function () {
    var cache;
    if (arguments.length === 1) {
        cache = arguments[0];  //如果arguments对象的长度为1,也就是参数只有1个时,将这个参数赋值给cache,暂存
        return function (number) {  //返回一个函数,函数里的参数(也就是第二个括号里的参数)与之前第一个括号里的参数相加
            return cache + number
        }
    } else {
        return arguments[0] + arguments[1];  //如果arguments对象的长度不为1,那么两个参数相加
    }
}
console.log(sum(2, 3));  //5
console.log(sum(2)(3));  //5
### 闭包的定义与特性 闭包是 JavaScript 中的核心概念,它由一个函数和其词法环境组成。词法环境包含函数声明时所在作用域中的所有局部变量。闭包允许内部函数访问外部函数的作用域,即使外部函数已经执行完毕。闭包的生命周期可以延长外部函数中的局部变量,这与普通函数在执行完毕后销毁局部变量的特性不同。闭包的这种特性使其在函数式编程中非常有用,但也可能导致内存占用较高[^2]。 ### 常见闭包面试题及解析 #### 面试题 1: 闭包中的变量访问 ```javascript var i = 10; function A() { var j = 20; var k = 30; function b() { var x = 40; var y = 50; function c() { console.log(i, j, k, x); } c(); } b(); } A(); console.log(k); ``` 在这个例子中,函数 `c` 是一个闭包,它可以访问外部函数 `b` 和 `A` 中的变量,以及全局变量 `i`。当调用 `A()` 时,`c()` 会输出 `10, 20, 30, 40`,因为 `c` 能够访问这些变量。最后的 `console.log(k)` 会输出 `30`,因为 `k` 是全局变量 `A` 中的局部变量,在 `A` 执行完毕后仍然存在于闭包中[^1]。 #### 面试题 2: 闭包与计数器 ```javascript function fun() { var count = 1; return function () { count++; console.log(count); }; } var fun2 = fun(); fun2(); // 输出 2 fun2(); // 输出 3 ``` 在这个例子中,`fun` 返回了一个内部函数,该函数形成了一个闭包,能够访问 `fun` 中的 `count` 变量。每次调用 `fun2()`,`count` 的值都会增加并输出。这展示了闭包如何延长外部函数中局部变量的生命周期[^3]。 #### 面试题 3: 闭包与块级作用域 ```javascript let count = 0; (function immediate() { if (count === 0) { let count = 1; console.log(count); // 输出 1 } console.log(count); // 输出 0 })(); ``` 在这个例子中,`immediate` 是一个立即执行的函数表达式。在 `if` 语句块中,`count` 被重新声明为一个新的局部变量,并赋值为 `1`。因此,`console.log(count)` 输出 `1`。而在 `if` 语句块外部,`count` 仍然是全局变量 `0`,因为块级作用域中的 `count` 不会影响外部的 `count`[^5]。 ### 闭包的应用场景 闭包在 JavaScript 中有许多高级应用场景,例如函数式编程、创建私有变量和方法等。通过闭包,可以实现数据的封装和隐藏,避免全局变量的污染。此外,闭包还可以用于实现缓存、事件处理等功能。 ### 闭包的注意事项 尽管闭包非常强大,但使用不当可能会导致内存泄漏。例如,如果一个 DOM 元素被删除,但仍然保留对事件监听器的引用,而事件监听器又引用了该 DOM 元素,就会导致内存泄漏。因此,在使用闭包时需要注意及时解绑事件监听器或释放不再需要的引用[^4]。 ### 总结 闭包是 JavaScript 中非常重要的概念,它允许内部函数访问外部函数的作用域,即使外部函数已经执行完毕。闭包的生命周期可以延长外部函数中的局部变量,这使其在函数式编程中非常有用。然而,闭包也可能导致内存占用较高,甚至引发内存泄漏,因此在使用时需要谨慎。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值