当前位置: 首页 > article >正文

闭包+面试真题

对闭包的理解

闭包是内层函数使用外层变量
(子级可以访问父级的变量,但是父级不可以访问子级的)

闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,创建的函数可以访问到当前函数的局部变量。

闭包有两个常用的用途;

  • 闭包的第一个用途是使我们在函数外部能够访问到函数内部的变量。通过使用闭包,可以通过在外部调用闭包函数,从而在外部访问到函数内部的变量,可以使用这种方法来创建私有变量。
  • 闭包的另一个用途是使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收。
    这是因为 JavaScript 的垃圾回收机制会检查对象是否还被引用,如果没有引用,则认为该对象是垃圾,可以被回收。而闭包的内部函数可能会继续访问外部函数的变量。只有当闭包函数本身也被销毁时,其引用的外部作用域才会被释放。
    比如,函数 A 内部有一个函数 B,函数 B 可以访问到函数 A 中的变量,那么函数 B 就是闭包。
function A() {
  let a = 1;
  window.B = function () {
    console.log(a);
  };
}
A();
B(); // 1

面试真题

1. 基础题

在 JS 中,闭包存在的意义就是让我们可以间接访问函数内部的变量。经典面试题:循环中使用闭包解决 var 定义函数的问题

for (var i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i);
  }, i * 1000);
}

首先因为 setTimeout 是个异步函数,所以会先把循环全部执行完毕,这时候 i 就是 6 了,所以会输出一堆 6。解决办法有三种:

  • 第一种是使用闭包的方式
for (var i = 1; i <= 5; i++) {
  (function (j) {
    setTimeout(function timer() {
      console.log(j);
    }, j * 1000);
  })(i); // 把i作为参数传递给前面括号里的j
}

使用立即执行函数包裹 setTimeout 的回调函数,能够创建一个闭包
在上述代码中,首先使用了立即执行函数i 传入函数内部,这个时候值就被固定在了参数 j 上面不会改变,当下次执行 timer 这个闭包的时候,就可以使用外部函数的变量 j,从而达到目的。

  • 第二种就是使用 setTimeout 的第三个参数,这个参数会被当成 timer 回调函数的参数传入。
for (var i = 1; i <= 5; i++) {
  setTimeout(
    function timer(j) {
      console.log(j);
    },
    i * 1000,
    i
  );
}
  • 第三种就是使用 let 定义 i 了来解决问题了,这个也是最为推荐的方式
for (let i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i);
  }, i * 1000);
}

2. 变形题

/* TASK A1
    In the following code, what will be printed to console if a user clicks the first and fourth button? Why?
*/

    var nodes = document.getElementsByTagName("button");
    for (var i = 0; i < nodes.length; i++) {
      nodes[i].addEventListener("click", function () {
        console.log("You clicked element #" + i);
      });
    }

输出的是 nodes.length的值;

因为var生命的是函数作用域

解决:
方法1:let
方法2:闭包

 var nodes = document.getElementsByTagName("button");
    for (var i = 0; i < nodes.length; i++) {
      (function (i) {
        nodes[i].addEventListener("click", function () {
          console.log("You clicked element #" + i);
        });
      })(i);
    }

http://www.kler.cn/a/305191.html

相关文章:

  • Thread类及常见方法
  • Zotero 6.0 安装包及安装教程
  • 【go从零单排】JSON序列化和反序列化
  • 建筑施工特种作业人员安全生产知识试题
  • 使用Docker快速部署FastAPI Web应用
  • Vue 的生命周期函数 和 Vuex
  • Python 数学建模——cvxpy 规划求解器
  • 在线编程实现!如何在Java后端通过DockerClient操作Docker生成python环境
  • 如何理解Configurational entropy
  • 你的大模型应用表现真的好吗?借助 Dify + Langfuse 一探究竟
  • Excel 基础知识-操作手册2
  • Python 实现Excel XLS和XLSX格式相互转换
  • nacos 安装 centos7 docker
  • 准备SAP RISE Go-Live weekend
  • Vue3+TypeScript+Vite+Less 开发 H5 项目(amfe-flexible + postcss-pxtorem)
  • ingress对外服务
  • c# socket通信实例
  • Docker突然宣布:涨价80%
  • 初阶数据结构【TOP】- 11.普通二叉树的介绍 - 1. (细致,保姆~~!)
  • 进阶岛 任务2:Lagent 自定义你的 Agent 智能体
  • sshpass 实现的SSH免交互密码登录和ARM移植
  • Java中List集合去重
  • 【python计算机视觉编程——9.图像分割】
  • 什么是 SMB 服务器以及它如何工作?
  • Nginx引发的惨案
  • 动手学深度学习(pytorch)学习记录28-使用块的网络(VGG)[学习记录]