• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

这 7 道闭包的面试题,你能答对几个

武飞扬头像
cj瑾瑜
帮助1

每个 JavaScript 程序员都必须知道闭包是什么。在 JavaScript 面试中,你很可能会被问到闭包的概念。

以下是 7 个有关 JavaScript 闭包的面试题,比较有挑战性。

不要查看答案或运行代码,看看自己的水平到底如何。做完这些题大约需要半小时左右。

1. 热身

有以下函数 clickHandlerimmediatedelayedReload

  1.  
    let countClicks = 0;
  2.  
    button.addEventListener('click', function clickHandler() {
  3.  
    countClicks ;
  4.  
    });
  5.  
    const result = (function immediate(number) {
  6.  
    const message = `number is: ${number}`;
  7.  
    return message;
  8.  
    })(100);
  9.  
    setTimeout(function delayedReload() {
  10.  
    location.reload();
  11.  
    }, 1000);

这3个函数中哪个能够访问外部范围变量?

答案

  1. clickHandler 能够从外部作用域访问变量 countClicks
  2. immediate 无法访问外部作用域中的任何变量。
  3. delayedReload 从全局作用域(也就是最外层作用域)中访问全局变量 location

2. 丢失的参数

下列代码输出什么:

  1.  
    (function immediateA(a) {
  2.  
    return (function immediateB(b) {
  3.  
    console.log(a); // => ?
  4.  
    })(1);
  5.  
    })(0);

答案

输出为:0

用参数 0 调用 immediateA,因此 a 参数为 0

immediateB 函数嵌套在 immediateA 函数中,是一个闭包,它从外部 immediateA 作用域中得到 a 变量,其中 a0。因此 console.log(a) 的输出为 0

3. 谁是谁

下面的代码将会输出什么内容?

  1.  
    let count = 0;
  2.  
    (function immediate() {
  3.  
    if (count === 0) {
  4.  
    let count = 1;
  5.  
    console.log(count); // 输出什么?
  6.  
    }
  7.  
    console.log(count); // 输出什么?
  8.  
    })();

答案

输出 10

第一个语句 let count = 0 声明了一个变量 count

immediate() 是一个闭包,它从外部作用域得到 count 变量。在 immediate() 函数作用域内, count0

但是,在条件内,另一个 let count = 1 声明了局部变量 count,该变量覆盖了作用域之外的 count。第一个 console.log(count) 输出 1

第二个 console.log(count) 输出为 0 ,因为这里的 count 变量是从外部作用域访问的。

4. 棘手的闭包

下列代码输出什么:

  1.  
    for (var i = 0; i < 3; i ) {
  2.  
    setTimeout(function log() {
  3.  
    console.log(i); // => ?
  4.  
    }, 1000);
  5.  
    }

答案

输出:3, 3, 3

代码分为两个阶段执行。

阶段1

  1. for() 重复 3 次。在每次循环都会创建一个新函数 log(),该函数将捕获变量 isetTimout() 安排log() 在 1000 毫秒后执行。
  2. for() 循环完成时,变量 i 的值为 3

阶段2

第二阶段发生在 1000ms 之后:

  1. setTimeout() 执行预定的 log() 函数。 log() 读取变量 i 当前的值 3,并输出 3

所以输出 3, 3, 3

5. 错误的信息

下面的代码将会输出什么:

  1.  
    function createIncrement() {
  2.  
    let count = 0;
  3.  
    function increment() {
  4.  
    count ;
  5.  
    }
  6.  
     
  7.  
    let message = `Count is ${count}`;
  8.  
    function log() {
  9.  
    console.log(message);
  10.  
    }
  11.  
     
  12.  
    return [increment, log];
  13.  
    }
  14.  
     
  15.  
    const [increment, log] = createIncrement();
  16.  
    increment();
  17.  
    increment();
  18.  
    increment();
  19.  
    log(); // => ?
学新通

答案

输出:'Count is 0'

increment() 函数被调用 3 次,将 count 增加到 3

message 变量存在于 createIncrement() 函数的作用域内。其初始值为 'Count is 0'。但即使 count 变量已经增加了几次,message 变量的值也始终为 'Count is 0'

log() 函数是一个闭包,它从 createIncrement() 作用域中获取 message 变量。 console.log(message) 输出录'Count is 0'到控制台。

6. 重新封装

下面的函数 createStack() 用于创建栈结构:

  1.  
    function createStack() {
  2.  
    return {
  3.  
    items: [],
  4.  
    push(item) {
  5.  
    this.items.push(item);
  6.  
    },
  7.  
    pop() {
  8.  
    return this.items.pop();
  9.  
    }
  10.  
    };
  11.  
    }
  12.  
     
  13.  
    const stack = createStack();
  14.  
    stack.push(10);
  15.  
    stack.push(5);
  16.  
    stack.pop(); // => 5
  17.  
     
  18.  
    stack.items; // => [10]
  19.  
    stack.items = [10, 100, 1000]; // 栈结构的封装被破坏了
学新通

它能正常工作,但有一个小问题,因为暴露了 stack.items 属性,所以任何人都可以直接修改 items 数组。

这是一个大问题,因为它破坏了栈的封装:应该只有 push()pop() 方法是公开的,而 stack.items 或其他任何细节都不能被访问。

使用闭包的概念重构上面的栈实现,这样就无法在 createStack() 函数作用域之外访问 items 数组:

  1.  
    function createStack() {
  2.  
    // 把你的代码写在这里
  3.  
    }
  4.  
     
  5.  
    const stack = createStack();
  6.  
    stack.push(10);
  7.  
    stack.push(5);
  8.  
    stack.pop(); // => 5
  9.  
     
  10.  
    stack.items; // => undefined

答案

以下是对 createStack() 的重构:

  1.  
    function createStack() {
  2.  
    const items = [];
  3.  
    return {
  4.  
    push(item) {
  5.  
    items.push(item);
  6.  
    },
  7.  
    pop() {
  8.  
    return items.pop();
  9.  
    }
  10.  
    };
  11.  
    }
  12.  
     
  13.  
    const stack = createStack();
  14.  
    stack.push(10);
  15.  
    stack.push(5);
  16.  
    stack.pop(); // => 5
  17.  
     
  18.  
    stack.items; // => undefined
学新通

items 已被移至 createStack() 作用域内。

这样修改后,从 createStack() 作用域的外部无法访问或修改 items 数组。现在 items 是一个私有变量,并且栈被封装:只有 push()pop() 方法是公共的。

push()pop() 方法是闭包,它们从 createStack() 函数作用域中得到 items 变量。

7. 智能乘法

编写一个函数 multiply() ,将两个数字相乘:

  1.  
    function multiply(num1, num2) {
  2.  
    // 把你的代码写在这里...
  3.  
    }

要求:

如果用 2 个参数调用 multiply(num1,numb2),则应返回这 2 个参数的乘积。

但是如果用 1个参数调用,则该函数应返回另一个函数: const anotherFunc = multiply(num1) 。返回的函数在调用 anotherFunc(num2) 时执行乘法 num1 * num2

  1.  
    multiply(4, 5); // => 20
  2.  
    multiply(3, 3); // => 9
  3.  
     
  4.  
    const double = multiply(2);
  5.  
    double(5); // => 10
  6.  
    double(11); // => 22

答案

以下是 multiply() 函数的一种实现方式:

  1.  
    function multiply(number1, number2) {
  2.  
    if (number2 !== undefined) {
  3.  
    return number1 * number2;
  4.  
    }
  5.  
    return function doMultiply(number2) {
  6.  
    return number1 * number2;
  7.  
    };
  8.  
    }
  9.  
     
  10.  
    multiply(4, 5); // => 20
  11.  
    multiply(3, 3); // => 9
  12.  
     
  13.  
    const double = multiply(2);
  14.  
    double(5); // => 10
  15.  
    double(11); // => 22
学新通

如果 number2 参数不是 undefined,则该函数仅返回 number1 * number2

但是,如果 number2undefined,则意味着已经使用一个参数调用了 multiply() 函数。这时就要返回一个函数 doMultiply(),该函数稍后被调用时将执行实际的乘法运算。

doMultiply() 是闭包,因为它从 multiply() 作用域中得到了number1 变量。

总结

如果你答对了 5 个以上,说明对闭包掌握的很好。

原文链接: 这 7 道关于闭包的面试题,你能答对几个?
作者:疯狂的技术宅

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgggajb
系列文章
更多 icon
同类精品
更多 icon
继续加载