2018.5 一周面试题

228 阅读7分钟

1、传统布局和flex布局有什么区别?

答:css盒子模型是所有布局共同的,不是某个布局专属的。目前主流的布局有两类,一类是基于格子布局,将页面看成行+列的二维布局。另一类是flex布局,将页面看成行或者列的一维布局。
性能上flex允许子元素不设置宽高,而是算法动态去计算,性能会比设定好的慢一些,但在这个时代没有大影响。但计算结果有时候会不符合预期,需要flex提供属性给与启发。
div + css、基于table的布局
拓展:grid、多列布局的多种方法、flex的历史写法、哪些浏览器支持-webkit-box、flex是w3c提议的还是标准、什么是布局、flex在移动端的兼容性
https://developer.mozilla.org/zh-CN/docs/Learn/CSS/CSS_layout

2、你对vue源码了解吗?双向绑定是怎么实现的?详细描述什么时候监听变化、什么时候触发变法?

答:
参考网址:https://juejin.cn/post/6844903605414133773

3、如何比较两个颜色的相似性?

答:这是一道开放题目,首先将颜色拆分成r/g/b三个值,如果是字符串的颜色如#aabbff或者rgb(255,128,100)可以用正则表达式取出对应的r/g/b值。对于16进制字符串,可以使用parseInt('0xaa')转10进制整数。然后对于两个颜色,可以使用距离 Math.sqrt( (r1-r2) *(r1-r2) +(g1-g2)*(g1-g2)+(b1-b2)*(b1-b2) )进行比较, 距离近则相似。 当然可以用Math.hypot( r1-r2, g1-t2, b1-b2) 来简化上述运算。
这道题目主要考察学员的知识积累和思考。 首先要知道rgb是颜色的组成。 然后要给出一种可行的比较方法。 最后要考察具体javascript细节函数的运用。

4、一个单页面应用,有6张页面,F、E、A、B、C、D。 页面ABCD构成了一个冗长的用户验证过程。目前A、B、C对应用户验证过程的第1步,第2步,第3步。 页面F是首页,E是某张业务相关页面。用户到达页面E后,系统发现用户没有认证,触发验证流程,到达页面A,然后开始A->B->C->D流程。 页面D是验证结果页面(验证成功页面)。 请问,如果到达页面D后,如何让用户点击返回可以返回页面F,而忽略中间流程(注:用户可能根本没有到达过F,比如微信分享直接进入了E)。

答: 这个问题初一看是对单页面路由架构的考察。也是一个很好的引入问题,可以考察非常多方面。 比如说:如何实现页面切换动画? A、B、C都是表单的话,如何缓存用户输入完成的表单数据?……回到问题,因为history api提供了push/pop/replace三种操作,无论是其中的任何一种都无法实现上述的效果。 一个路由系统,首先要监听浏览器地址的变化,然后根据变化渲染不同的页面。1. 在页面到达D后,关闭对路由变化页面渲染的监听。 2. 路由系统要进行多次POP,可以用history.go(-n)实现; 3. 路由栈清空到只剩下一张页面时,将这张页面替换为F。 4. PUSH一张页面D。 5. 如果在HTML上有一个类似「轮播图」的设计,就是每一张页面是一张轮播图,将轮播图设置成只有「F」和「D」。 5. 恢复路由监听。 这个问题的另一个考点是,在上述完整的计算过程当中,需要知道当前历史记录中的页面数,页面数可以通过localStorage实现,在ls中维护一个变量,每次push的时候+1,并写入history.state。 POP的时候读取history.state将变量重置。

5、一个无序正负项 数组, eg:[3, -6, 123, -945, -231, 112] 找出其中的最大的连续子序列

有如下乱序数组 A1, A2, A3, A4,........An, 求 i, j (1<= i <= j<= n), 使得Ai + .... + Aj 和最大, 输出i j

答:思路:假设答案是i,j;那么第一个数到第i-1个数之和一定是一个负数;。最大子序列两端的端点到整个数组的断点的值之和 一定是负数。

6、函数的节流与防抖

答:你是否在日常开发中遇到一个问题,在滚动事件中需要做个复杂计算或者实现一个按钮的防二次点击操作。
这些需求都可以通过函数防抖动来实现。尤其是第一个需求,如果在频繁的事件回调中做复杂计算,很有可能导致页面卡顿,不如将多次计算合并为一次计算,只在一个精确点做操作。因为防抖动的轮子很多,这里也不重新自己造个轮子了,直接使用 underscore 的源码来解释防抖动。
整体函数实现的不难,总结一下。
对于按钮防点击来说的实现:一旦我开始一个定时器,只要我定时器还在,不管你怎么点击都不会执行回调函数。一旦定时器结束并设置为 null,就可以再次点击了。
对于延时执行函数来说的实现:每次调用防抖动函数都会判断本次调用和之前的时间间隔,如果小于需要的时间间隔,就会重新创建一个定时器,并且定时器的延时为设定时间减去之前的时间间隔。一旦时间到了,就会执行相应的回调函数。
节流
防抖动和节流本质是不一样的。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。
/**
 * underscore 节流函数,返回函数连续调用时,func 执行频率限定为 次 / wait
 *
 * @param  {function}   func      回调函数
 * @param  {number}     wait      表示时间窗口的间隔
 * @param  {object}     options   如果想忽略开始函数的的调用,传入{leading: false}。
 *                                如果想忽略结尾函数的调用,传入{trailing: false}
 *                                两者不能共存,否则函数不能执行
 * @return {function}             返回客户调用函数   
 */
_.throttle = function(func, wait, options) {
    var context, args, result;
    var timeout = null;
    // 之前的时间戳
    var previous = 0;
    // 如果 options 没传则设为空对象
    if (!options) options = {};
    // 定时器回调函数
    var later = function() {
      // 如果设置了 leading,就将 previous 设为 0
      // 用于下面函数的第一个 if 判断
      previous = options.leading === false ? 0 : _.now();
      // 置空一是为了防止内存泄漏,二是为了下面的定时器判断
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    };
    return function() {
      // 获得当前时间戳
      var now = _.now();
      // 首次进入前者肯定为 true
	  // 如果需要第一次不执行函数
	  // 就将上次时间戳设为当前的
      // 这样在接下来计算 remaining 的值时会大于0
      if (!previous && options.leading === false) previous = now;
      // 计算剩余时间
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;
      // 如果当前调用已经大于上次调用时间 + wait
      // 或者用户手动调了时间
 	  // 如果设置了 trailing,只会进入这个条件
	  // 如果没有设置 leading,那么第一次会进入这个条件
	  // 还有一点,你可能会觉得开启了定时器那么应该不会进入这个 if 条件了
	  // 其实还是会进入的,因为定时器的延时
	  // 并不是准确的时间,很可能你设置了2秒
	  // 但是他需要2.2秒才触发,这时候就会进入这个条件
      if (remaining <= 0 || remaining > wait) {
        // 如果存在定时器就清理掉否则会调用二次回调
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      } else if (!timeout && options.trailing !== false) {
        // 判断是否设置了定时器和 trailing
	    // 没有的话就开启一个定时器
        // 并且不能不能同时设置 leading 和 trailing
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  };

extra: json web token,jwt