• 一道经典JS面试题


    见过这道题吗?

    <!DOCTYPE html>
    <html>
    <head>
    <meta name="description" content="iMagic">
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width">
      <title>JS Bin</title>
    </head>
    <body>
      <h2>点击以下三个li,输出分别是什么?</h2>
      <ul>
        <li>Item 0</li>
        <li>Item 1</li>
        <li>Item 2</li>
      </ul>
      <script>
      var lis = document.getElementsByTagName('li');
    
      for (var i = 0; i < lis.length; i++) {
        lis[i].onclick = function (event) {
          console.log('你点击了 ' + i);
        }
      }
      </script>
    </body>
    </html>

    第一个问题,点击三个li,console输出结果是什么?(答错扣30分)

    # 聪明的你这时点击了三个li
    
    "你点击了 3"
    "你点击了 3"
    "你点击了 3"
    

    (⊙⊙!) 

    第二个问题,为什么输出总是“3”呢?(答错扣30分,可以考虑提前结束面试)

    我们祭出console.log大法:

     var lis = document.getElementsByTagName('li');
    
      for (var i = 0; i < lis.length; i++) {
        console.log('添加第' + i + '个onclick listener');
    
        lis[i].onclick = function (event) {
          console.log(
            '只有点击时才进入onclick这个function, 这时i已经变成了' + i);
          console.log('你点击了 ' + i);
        }
    
        console.log('i这时变成了' + i);
      }
    
      console.log('i在for循环结束后变成了' + i);
    "添加第0个onclick listener"
    "i这时变成了0"
    
    "添加第1个onclick listener"
    "i这时变成了1"
    
    "添加第2个onclick listener"
    "i这时变成了2"
    
    "i在for循环结束后变成了3"
    
    # 聪明的你这时点击了一个li
    
    "只有点击时才进入onclick这个function, 这时i已经变成了3"
    "你点击了 3"

    第三个问题,如何修复这段代码,确保在点击Item 0, 1, 2的时候分别输出0, 1, 2?(答不出来扣40分,结束面试,每给出一种解法加30分)

    第一种解法:利用closure对每一个 onclick event handler 建立一个其专属的 function,并在建立每个function的时候保存住 i 的值:

     var lis = document.getElementsByTagName('li');
    
      for (var i = 0; i < lis.length; i++) {
    
        lis[i].onclick = (function (invokedi) {
          console.log('现在建立对于Item' + invokedi + '的event handler');
          return function uniqEventHandlerFori(event){
            console.log(
              'invokedi是在建立event handler时传入的,它的值是' +
              invokedi);
            console.log('你点击了 ' + invokedi);
          }      
        }(i))
    
      }

    结果:

    "现在建立对于Item0的event handler"
    "现在建立对于Item1的event handler"
    "现在建立对于Item2的event handler"
    
    # 聪明的你这时点击了三个li
    
    "invokedi是在建立event handler时传入的,它的值是0"
    "你点击了 0"
    
    "invokedi是在建立event handler时传入的,它的值是1"
    "你点击了 1"
    
    "invokedi是在建立event handler时传入的,它的值是2"
    "你点击了 2"
    

     

    第二种解法利用 Function.prototype.bind(thisArg, params...) 对每一个 onclick event handler 建立一个其专属的 function,并在建立每个function的时候保存住 i 的值:

    var lis = document.getElementsByTagName('li');
    
      for (var i = 0; i < lis.length; i++) {
    
        lis[i].onclick = function handlerToBind (event){
          console.log(
            'function handlerToBind(){...}.bind(i) --> ' +
            ' 建立了一个新的function,并且在这个函数里面,' +
            'this === 运行bind(i)时i的值 === ' + this);
            
          console.log('你点击了 ' + this);
        }.bind(i); 
    
      }

    结果:# 聪明的你这时点击了三个li

    
    "function handlerToBind(){...}.bind(i) -->  建立了一个新的function,并且在这个函数里面,this === 运行bind(i)时i的值 === 0"
    "你点击了 0"
    
    "function handlerToBind(){...}.bind(i) -->  建立了一个新的function,并且在这个函数里面,this === 运行bind(i)时i的值 === 1"
    "你点击了 1"
    
    "function handlerToBind(){...}.bind(i) -->  建立了一个新的function,并且在这个函数里面,this === 运行bind(i)时i的值 === 2"
    "你点击了 2"

    第三种解法event.target 拿到实际被点击的 li,然后利用 Array.prototype.indexOf(item) 去查找它的index。
    var lis = document.getElementsByTagName('li');
      
      console.log(
        '通过document.getElementsByTagName返回的lis' + 
        '并没有返回真正的Array, 只是一个Array-like object. ' + 
        '我们需要把它构建成一个真正的Array才能在待会用indexOf方法. ' );
        
      var lisArray = Array.prototype.slice.call(lis);
          
      console.log('现在你可以愉快的使用lisArray.indexOf(item)了');
        
      for (var i = 0; i < lis.length; i++) {
    
        lis[i].onclick = function handlerToBind (event){
          
          console.log(
            'event.target 就是被点击的 DOM Element: ' +
            event.target.innerHTML + 
            '. 它存在于lis和lisArray中');
            
          console.log('你点击了 ' + lisArray.indexOf(event.target));
        }; 
    
      }

    结果:

     "通过document.getElementsByTagName返回的lis并没有返回真正的Array, 只是一个Array-like object. 我们需要把它构建成一个真正的Array才能在待会用indexOf方法. "

    "现在你可以愉快的使用lisArray.indexOf(item)了"
    
    # 聪明的你这时点击了三个li
    
    "event.target 就是被点击的 DOM Element: Item 0. 它存在于lis和lisArray中"
    "你点击了 0"
    
    "event.target 就是被点击的 DOM Element: Item 1. 它存在于lis和lisArray中"
    "你点击了 1"
    
    "event.target 就是被点击的 DOM Element: Item 2. 它存在于lis和lisArray中"
    "你点击了 2"

    (重点)第四种解法:使用ES6的 let,把 i 限定在block level里面 (单独给出此解法加10分,如果同时给出第一或第二种解法,此解法加30分)
     var lis = document.getElementsByTagName('li');
    
      for (let i = 0; i < lis.length; i++) {
        lis[i].onclick = function (event) {
          console.log('你点击了 ' + i);
        }
      }

    结果:

    # 聪明的你这时点击了三个li
    
    "你点击了 0"
    "你点击了 1"
    "你点击了 2"
    参考:https://zhuanlan.zhihu.com/p/24024766
  • 相关阅读:
    [学习笔记&教程] 信号, 集合, 多项式, 以及各种卷积性变换 (FFT,NTT,FWT,FMT)
    [学习笔记] CDQ分治&整体二分
    [日常] NOIp 2018 滚粗记
    [学习笔记] 模拟退火 (Simulated Annealing)
    [日常] NOIWC 2018爆零记
    [日常] PKUWC 2018爆零记
    [日常] 最近的一些破事w...
    [BZOJ 1877][SDOI2009]晨跑
    [COGS 2583]南极科考旅行
    [日常] NOIP 2017滚粗记
  • 原文地址:https://www.cnblogs.com/indigojh/p/7515525.html
Copyright © 2020-2023  润新知