zoukankan      html  css  js  c++  java
  • 面试常见问题(一)

    问题 #1: 事件委托

    也叫事件委派,时间代理等

    当构建应用程序时,有时你需要将事件监听器绑定到页面上的按钮,文本或图像上,以便在用户与元素交互时执行某些操作。

    如果我们以一个简单的待办事项列表为例,面试官可能会告诉你,他们希望在用户单击其中一个列表项时需要执行某些操作。 他们希望你用 JavaScript 实现这个功能,假设HTML代码如下:

    <ul id="todo-app">
      <li class="item">Walk the dog</li>
      <li class="item">Pay bills</li>
      <li class="item">Make dinner</li>
      <li class="item">Code for one hour</li>
    </ul>
    

      你可能会想像下面这样在元素绑定事件监听器:

    document.addEventListener('DOMContentLoaded', function() {
      
      let app = document.getElementById('todo-app');
      let items = app.getElementsByClassName('item');
      
      // 将事件侦听器绑定到每个列表项
      for (let item of items) {
        item.addEventListener('click', function() {
          alert('you clicked on item: ' + item.innerHTML);
        });
      }
      
    });
    

      

    虽然这个实现了功能,问题是您要单独将事件侦听器绑定到每个列表项。这是4个元素,没什么大问题,但如果有人在他们的待办事项列表中添加了10,000个事项(他们可能有很多事情要做)怎么办?然后你的函数将创建 10,000 个独立的事件监听器,并将每个事件监听器绑定到 DOM 。这样代码执行的效率非常低下

    在面试中,最好首先询问面试官用户可以输入事项的最大数量是多少。如果它永远不会超过 10 个,上面的代码将工作正常。但是,如果用户可以输入的事项数量没有限制,那么你应该使用一个更高效的解决方案。

    如果你的应用程序最终可能有几百个事件监听器,更高效的解决方案是将一个事件侦听器实际绑定到整个容器上,然后在实际单击时可以访问每个确切元素。这被称为事件委托,并且它每个元素单独绑定事件处理程序更高效。

    用事件委托的代码:

    document.addEventListener('DOMContentLoaded', function() {
      
      let app = document.getElementById('todo-app');
      
      // 事件侦听器绑定到整个容器上
      app.addEventListener('click', function(e) {
        if (e.target && e.target.nodeName === 'LI') {
          let item = e.target;
          alert('you clicked on item: ' + item.innerHTML);
        }
      });
      
    });
    

      

    在循环内使用闭包(Closures)

    闭包常常在面试中出现,以便面试官衡量你对这门语言的熟悉程度,以及是否知道何时使用闭包。

    闭包的本质是一个内部函数访问其作用域之外的变量。闭包可以用于实现诸如 私有变量 和 创建工厂函数之类的东西。关于使用闭包的常见面试问题是这样的:

    编写一个函数,它将循环遍历整数列表,并在3秒延迟后打印每个元素的索引。

    我看到这个问题的最常见(但是不正确)是像下面这样的实现:

    const arr = [10, 12, 15, 21];
    for (var i = 0; i < arr.length; i++) {
      setTimeout(function() {
        console.log('The index of this number is: ' + i);
      }, 3000);
    }
    

      

    如果运行上面代码,3秒延迟后你会看到,实际上每次打印输出是4,而不是期望的0,1,2,3 。

    为了正确理解为什么会发生这种情况,在JavaScript中很有用,这正是面试官真正的意图。

    其原因是因为 setTimeout 函数创建了一个可以访问其外部作用域的函数(也就是我们经常说的闭包),每个循环都包含了索引i
    3秒后,该函数被执行并且打印出i的值,其在循环结束时为4,因为它的循环周期经历了0,1,2,3,4,并且循环最终在4时停止。

    实际有几种 正确的写法来解决这个问题,下面列举两种:

    const arr = [10, 12, 15, 21];
    for (var i = 0; i < arr.length; i++) {
      // 通过传递变量 i
      // 在每个函数中都可以获取到正确的索引
      setTimeout(function(i_local) {
        return function() {
          console.log('The index of this number is: ' + i_local);
        }
      }(i), 3000);
    }
    

      

    const arr = [10, 12, 15, 21];
    for (let i = 0; i < arr.length; i++) {
      // 使用ES6的let语法,它会创建一个新的绑定
      // 每个方法都是被单独调用的
      // 更多详细信息请阅读: http://exploringjs.com/es6/ch_variables.html#sec_let-const-loop-heads
      setTimeout(function() {
        console.log('The index of this number is: ' + i);
      }, 3000);
    }
    

      

    函数防抖动(Debouncing)

    函数防抖动(Debouncing) 是解决这个问题的一种方式,通过限制需要经过的时间,直到再次调用函数。一个正确实现函数防抖的方法是:把多个函数放在一个函数里调用,隔一定时间执行一次。这里有一个使用原生JavaScript实现的例子,用到了作用域、闭包、this定时事件

    // debounce函数用来包裹我们的事件
    function debounce(fn, delay) {
      // 持久化一个定时器 timer
      let timer = null;
      // 闭包函数可以访问 timer
      return function() {
        // 通过 'this' 和 'arguments'
        // 获得函数的作用域和参数
        let context = this;
        let args = arguments;
        // 如果事件被触发,清除 timer 并重新开始计时
        clearTimeout(timer);
        timer = setTimeout(function() {
          fn.apply(context, args);
        }, delay);
      }
    }
    

      

    当这个函数绑定在一个事件上,只有经过一段指定的时间后才会被调用。

    你可以像这样去使用这个函数:

    // 当用户滚动时函数会被调用
    function foo() {
      console.log('You are scrolling!');
    }
     
    // 在事件触发的两秒后,我们包裹在debounce中的函数才会被触发
    let elem = document.getElementById('container');
    elem.addEventListener('scroll', debounce(foo, 2000));
    

      函数节流是另一个类似函数防抖的技巧,除了使用等待一段时间再调用函数的方法,函数节流还限制固定时间内只能调用一次。所以一个事件如果在100毫秒内发生10次,函数节流会每2秒调用一次函数,而不是100毫秒内全部调用。

  • 相关阅读:
    CSP-S2020总结
    题解-P6687 论如何玩转 Excel 表格
    题解-UVA12995 【Farey Sequence】
    题解-P4159 [SCOI2009] 【迷路】
    题解-SP2916【GSS5
    102. 二叉树的层序遍历
    力扣 160 相交链表 快慢指针 双指针
    3. 无重复字符的最长子串 滑动窗口
    最大连续1的个数 III
    B树和B+树
  • 原文地址:https://www.cnblogs.com/laneyfu/p/6544420.html
Copyright © 2011-2022 走看看