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

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

不要查看答案或运行代码,看看本人的程度到底如何。做完这些题大概须要半小时左右。

1. 热身

有以下函数 clickHandlerimmediatedelayedReload

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个函数中哪个可能拜访内部范畴变量?

答案

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

2. 失落的参数

下列代码输入什么:

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

答案

输入为:0

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

immediateB 函数嵌套在 immediateA 函数中,是一个闭包,它从内部 immediateA 作用域中失去 a 变量,其中 a0。因而 console.log(a) 的输入为 0

3. 谁是谁

上面的代码将会输入什么内容?

let count = 0;(function immediate() {  if (count === 0) {    let count = 1;    console.log(count); // 输入什么?  }  console.log(count); // 输入什么?})();

答案

输入 10

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

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

然而,在条件内,另一个 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

  1. for() 反复 3 次。在每次循环都会创立一个新函数 log(),该函数将捕捉变量 isetTimout() 安顿log() 在 1000 毫秒后执行。
  2. for() 循环实现时,变量 i 的值为 3

阶段2

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

  1. 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(); // => 5stack.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(); // => 5stack.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(); // => 5stack.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); // => 20multiply(3, 3); // => 9const double = multiply(2);double(5);  // => 10double(11); // => 22

答案

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

function multiply(number1, number2) {  if (number2 !== undefined) {    return number1 * number2;  }  return function doMultiply(number2) {    return number1 * number2;  };}multiply(4, 5); // => 20multiply(3, 3); // => 9const double = multiply(2);double(5);  // => 10double(11); // => 22

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

然而,如果 number2undefined,则意味着曾经应用一个参数调用了 multiply() 函数。这时就要返回一个函数 doMultiply(),该函数稍后被调用时将执行理论的乘法运算。

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

总结

如果你答对了 5 个以上,阐明对闭包把握的很好

如果你答对了不到 5 个,则须要好好的温习一下了。我倡议查看我的文章[JavaScript闭包的简略阐明](https://dmitripavlutin.com/si...)。


本文首发微信公众号:前端先锋

欢送扫描二维码关注公众号,每天都给你推送陈腐的前端技术文章


欢送持续浏览本专栏其它高赞文章:

  • 深刻了解Shadow DOM v1
  • 一步步教你用 WebVR 实现虚拟现实游戏
  • 13个帮你进步开发效率的古代CSS框架
  • 疾速上手BootstrapVue
  • JavaScript引擎是如何工作的?从调用栈到Promise你须要晓得的所有
  • WebSocket实战:在 Node 和 React 之间进行实时通信
  • 对于 Git 的 20 个面试题
  • 深刻解析 Node.js 的 console.log
  • Node.js 到底是什么?
  • 30分钟用Node.js构建一个API服务器
  • Javascript的对象拷贝
  • 程序员30岁前月薪达不到30K,该何去何从
  • 14个最好的 JavaScript 数据可视化库
  • 8 个给前端的顶级 VS Code 扩大插件
  • Node.js 多线程齐全指南
  • 把HTML转成PDF的4个计划及实现

  • 更多文章...