web前端Javascript—7道关于闭包的面试题
小职 2021-03-05 来源 : 阅读 524 评论 0

摘要:本文主要是web前端Javascript—7道关于闭包的面试题,附上相关问题以及解决答案,希望对大家web前端Javascript闭包的学习有所帮助。

本文主要是web前端Javascript—7道关于闭包的面试题,附上相关问题以及解决答案,希望对大家web前端Javascript闭包的学习有所帮助。

web前端Javascript—7道关于闭包的面试题

每个 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个函数中哪个能够访问外部范围变量?

 

答案:

 

clickHandler 能够从外部作用域访问变量 countClicks。

immediate 无法访问外部作用域中的任何变量。

delayedReload 从全局作用域(也就是最外层作用域)中访问全局变量 location。

2. 丢失的参数

 

下列代码输出什么:

 

(function immediateA(a) {

  return (function immediateB(b) {

    console.log(a); // => ?

  })(1);

})(0);

答案:

 

输出为:0

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

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

3. 谁是谁

 

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

 

let count = 0;

(function immediate() {

  if (count === 0) {

    let count = 1;

    console.log(count); // 输出什么?

  }

  console.log(count); // 输出什么?

})();

答案:

 

输出 1 和 0

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

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

但是,在条件内,另一个 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);

}

答案输出:

 

3, 3, 3。

代码分为两个阶段执行。

阶段1:

 

for() 重复 3 次。在每次循环都会创建一个新函数 log(),该函数将捕获变量 i。setTimout() 安排log() 在 1000 毫秒后执行。

当 for() 循环完成时,变量 i 的值为 3。

阶段2:

 

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

 

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

所以输出 3, 3, 3。

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'

 

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() 用于创建栈结构:

 

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 变量。


我是小职,记得找我

✅ 解锁高薪工作

✅ 免费获取学习教程,开发工具,代码大全,参考书籍

web前端Javascript—7道关于闭包的面试题

本文由 @小职 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程