每个 JavaScript 程序员都必须知道闭包是什么。在 JavaScript 面试中,你很可能会被问到闭包的概念。
以下是 7 个有关 JavaScript 闭包的面试题,比较有挑战性。
不要查看答案或运行代码,看看自己的水平到底如何。做完这些题大约需要半小时左右。
1. 热身
有以下函数 clickHandler,immediate和delayedReload:
- let countClicks = 0;
- button.addEventListener('click', function clickHandler() {
- countClicks++;
- });
- const result = (function immediate(number) {
- const message = `number is: ${number}`;
- return message;
- })(100);
- setTimeout(function delayedReload() {
- location.reload();
- }, 1000);
这3个函数中哪个能够访问外部范围变量?
答案:
2. 丢失的参数
下列代码输出什么:
- (function immediateA(a) {
- return (function immediateB(b) {
- console.log(a); // => ?
- })(1);
- })(0);
答案:
3. 谁是谁
下面的代码将会输出什么内容?
- let count = 0;
- (function immediate() {
- if (count === 0) {
- let count = 1;
- console.log(count); // 输出什么?
- }
- console.log(count); // 输出什么?
- })();
答案:
但是,在条件内,另一个 let count = 1 声明了局部变量 count,该变量覆盖了作用域之外的 count。第一个 console.log(count) 输出 1。
第二个 console.log(count) 输出为 0 ,因为这里的 count 变量是从外部作用域访问的。
4. 棘手的闭包
下列代码输出什么:
- for (var i = 0; i < 3; i++) {
- setTimeout(function log() {
- console.log(i); // => ?
- }, 1000);
- }
答案输出:
阶段1:
阶段2:
第二阶段发生在 1000ms 之后:
5. 错误的信息
下面的代码将会输出什么:
- function createIncrement() {
- let count = 0;
- function increment() {
- count++;
- }
- let message = `Count is ${count}`;
- function log() {
- console.log(message);
- }
- return [increment, log];
- }
- const [increment, log] = createIncrement();
- increment();
- increment();
- increment();
- log(); // => ?
答案:
输出:'Count is 0'
6. 重新封装
下面的函数 createStack() 用于创建栈结构:
- function createStack() {
- return {
- items: [],
- push(item) {
- this.items.push(item);
- },
- pop() {
- return this.items.pop();
- }
- };
- }
- const stack = createStack();
- stack.push(10);
- stack.push(5);
- stack.pop(); // => 5
- stack.items; // => [10]
- stack.items = [10, 100, 1000]; // 栈结构的封装被破坏了
它能正常工作,但有一个小问题,因为暴露了 stack.items 属性,所以任何人都可以直接修改 items 数组。
这是一个大问题,因为它破坏了栈的封装:应该只有 push() 和 pop() 方法是公开的,而 stack.items 或其他任何细节都不能被访问。
使用闭包的概念重构上面的栈实现,这样就无法在 createStack() 函数作用域之外访问 items 数组:
- function createStack() {
- // 把你的代码写在这里
- }
- const stack = createStack();
- stack.push(10);
- stack.push(5);
- stack.pop(); // => 5
- stack.items; // => undefined
答案:
以下是对 createStack() 的重构:
- function createStack() {
- const items = [];
- return {
- push(item) {
- items.push(item);
- },
- pop() {
- return items.pop();
- }
- };
- }
- const stack = createStack();
- stack.push(10);
- stack.push(5);
- stack.pop(); // => 5
- stack.items; // => undefined
items 已被移至 createStack() 作用域内。
这样修改后,从 createStack() 作用域的外部无法访问或修改 items 数组。现在 items 是一个私有变量,并且栈被封装:只有 push() 和 pop() 方法是公共的。
push() 和 pop() 方法是闭包,它们从 createStack() 函数作用域中得到 items变量。
7. 智能乘法
编写一个函数 multiply() ,将两个数字相乘:
- function multiply(num1, num2) {
- // 把你的代码写在这里...
- }
要求:
如果用 2 个参数调用 multiply(num1,numb2),则应返回这 2 个参数的乘积。
但是如果用 1个参数调用,则该函数应返回另一个函数:const anotherFunc = multiply(num1) 。返回的函数在调用 anotherFunc(num2) 时执行乘法 num1 * num2。
- multiply(4, 5); // => 20
- multiply(3, 3); // => 9
- const double = multiply(2);
- double(5); // => 10
- double(11); // => 22
答案:
以下是 multiply() 函数的一种实现方式:
- function multiply(number1, number2) {
- if (number2 !== undefined) {
- return number1 * number2;
- }
- return function doMultiply(number2) {
- return number1 * number2;
- };
- }
- multiply(4, 5); // => 20
- multiply(3, 3); // => 9
- const double = multiply(2);
- double(5); // => 10
- double(11); // => 22
如果 number2 参数不是 undefined,则该函数仅返回 number1 * number2。
但是,如果 number2 是 undefined,则意味着已经使用一个参数调用了 multiply() 函数。这时就要返回一个函数 doMultiply(),该函数稍后被调用时将执行实际的乘法运算。
doMultiply() 是闭包,因为它从 multiply() 作用域中得到了number1 变量。
TOP云 (west.cn)1月25日消息,近日,功夫贷宣布获得4000万人民币A轮融资,本轮...
描述 你正在和你的朋友玩 猜数字 (Bulls and Cows)游戏:你写下一个数字让你的朋...
hk 域名 哪里注册? .hk域名 在国内是可以注册的,只要提供了.hk 域名注册 服务...
我们知道效能提升 就是要应用系统方法实践和工具 通过它们改进技术、工程能力和...
本文介绍了北京慧达天下如何使用运维编排OOS提高发布效率。 公司介绍 公司名称:...
作者 | 亮言 来源 | 阿里技术公众号 一 背景 订单状态流转是交易系统的最为核心...
本文由网易云音乐实时计算平台研发工程师岳猛分享,主要从以下四个部分将为大家...
背景 2020年9月16日 Snowflake成功IPO 交易首日市场估值达到704亿美元 募集资金3...
约束与限制 只有运行中的云服务器云主机才允许用户登录。 Windows操作系统用户名...
有很多人在听说大数据之后,会开始纠结JAVA与大数据的区别,甚至还在纠结Java和...