前端部分面试题解(不定期更新)

已解决

  • Q: 用JS实现一个函数,能使输入特定的字符串转化为特定结构化数据。

    字符串仅由小写字母和[,]构成,且字符串不包含多余的空格

    示例一:'abc' -> {value:'abc'}

    示例二:'[abc[bcd[def]]]'-> {value:'abc',children:{value:'bcd',children:{value:'def'}}}

    A:

    // 类比AST,将输入转换为一系列操作
    function normalize(str) {
      let parent = {}
      // 所有括回均不需要,直接移除
      parent.children = _normalize(str.replace(/\]/g, ''))
      return parent.children
    }
    
    function _normalize(str) {
      // [abc[bcd[def]]] => {value: "abc", children: {value: "bcd", children: {value: "def"}}}
      // 不具有树形结构直接返回
      let obj = {}
      if(str.charAt(0) !== '[') {
        obj.value = str
        return obj
      }
      // '['开始, 找到之后的连续字符串
      let arr = str.split('[')
      let _val = arr[1]
      // 截取剩下的部分再进行递归
      let rest = ''
      for(let i = 2; i < arr.length; i++) {
        rest = rest + '[' +arr[i]
      }
      if (rest === '') {
        return {value: _val}
      }
      return {value: _val, children: _normalize(rest)}
    }
    
    normalize('[abc[bcd[def]]]')
    // normalize('def')
  • Q: 模仿lodash内调用curry函数,实现一个myCurry?

    A:

    墙裂推荐这篇译文,将柯里化的思考过程逐步展现出来

    本题解法来自《JavaScript轻量级函数式编程》

    // lodash: _.curry(func, [arity=func.length])
    // 实现两点:1. 参数复用——可固定参数,2. 延迟计算——返回最终函数触发计算
    // 原型实现:
    function curry(fn, arity = fn.length) {
      return (function nextCurried(prevArgs) {
        return function curried(nextArg) {
          var args = prevArgs.concat([nextArg])
          if(args.length >= arity) {
            return fn(...args)
          } else {
            return nextCurried(args)
          }
        }
      })([])
    }
    // 注意:偏函数partial(add, 3)和柯里化curry(add)(3)的区别
  • Q: 在不使用slice,replace等原生函数的情况下,实现一个indexOf方法(本题来自“司徒正美”的博客)?

    A:

    function getSubStrArr(str, length) {
      var subStrArr = []
      if(!length || length === 0) return subStrArr;
      for (var i = 0; i < str.length - length + 1; i++) {
        if(length > 1) {
          var tmp = [];
          for(var j = 0; j < length; j++) {
            tmp.push(str.charAt(i + j));
          }
          subStrArr.push(tmp.join(''));
        } else {
          subStrArr.push(str.charAt(i));
        }
      }
      return subStrArr;
    }
    
    function myIndexOf(str, target) {
      if(target === '' || target === undefined) return flag;
      var flag = -1
      var subStrArr = getSubStrArr(str, target.length);
      for(var i = 0; i < subStrArr.length; i++) {
        if(subStrArr[i] === target) {
          flag = i
        }
      }
      return flag
    }
    console.log(myIndexOf('abcdefgh', 'cde'))
  • Q: 简单手写实现bind

    A: 见 实现bind

  • Q: 如何获取两个时间(格式如: 2001-01-30)中间相差的年月日(提供三个精度)

    A:

    // 这道题投机的方式是通过字符串与年月日格式的硬匹配完成,但是如果要求多种精度,则需要很繁琐,建议使用Date
    function parseDate(str) {
      let strArr = str.split('-')
      if(strArr.length !== 3 || parseInt(strArr[0]) < 1970 || parseInt(strArr[1]) > 11 || parseInt(strArr[2]) > 31 ) { // 简单处理,兼容如果当月没有此日期
        throw new Error('日期格式非法')
      }
      let date = new Date(strArr[0], strArr[1]-1, strArr[2])
      return date;
    }
    
    // 深拷贝日期对象
    function dateToString(_date, unit) {
      var _d = new Date(_date.valueOf());
      var _dStr = ''
      if(unit > 0) _dStr += _d.getFullYear();
      if(unit > 1) _dStr += ('-' + (parseInt(_d.getMonth()) + 1 >= 10 ? (parseInt(_d.getMonth()) + 1).toString() : '0' + (parseInt(_d.getMonth()) + 1).toString()));
      if(unit > 2) _dStr += ('-' + (parseInt(_d.getDate()) >= 10 ? parseInt(_d.getDate()).toString() : '0' + parseInt(_d.getDate()).toString()));
      return _dStr;
    }
    
    // unit指精度, 从1-3分别指年月日,函数返回一个数组,包含中间所有的相差的日子
    function minorDates(start, end, unit) {
      var _u = unit || 3;
      var _start = parseDate(start)
      var _end = parseDate(end)
      var _minors = []
      var _minorDate = _end - _start
      var step = 24 * 60 * 60 * 1000; // default 一天
      if(_minorDate <= 0) {
        return _minors
      } else {
        for(var i = 0; i < _minorDate / step; i++) {
          var _d = dateToString(new Date(_start.getTime() + i * step), unit)
          if(_minors.indexOf(_d) === -1) _minors.push(_d);
        }
      }
      return _minors
    }
    
    console.log(minorDates('2019-02-25', '2020-02-25', 3))

来自前端面试周报的题

本篇主要记录 Github airuikun/Weekly-FE-Interview 仓库内的优秀面试题我自己的解法,会持续更新,但是不会每题都解一遍,建议可以访问源仓库。

  • Q: 简单手写实现Promise

    A:

    // 实现主要三个功能,thenable,resolve,reject,catch,finally
  • Q: 简单手写实现Async/Await

    A:

掘金 日常浏览的题目

  • Q: Promise题

    限制异步操作的并发个数并尽可能快的完成全部

    有8个图片资源的url,已经存储在数组urls中。

    urls类似于['https://image1.png', 'https://image2.png', ....]

    而且已经有一个函数function loadImg,输入一个url链接,返回一个Promise,该Promise在图片下载完成的时候resolve,下载失败则reject。 但有一个要求,任何时刻同时下载的链接数量不可以超过3个。请写一段代码实现这个需求,要求尽可能快速地将所有图片下载完成。

    作者:LinDaiDai_霖呆呆

    链接:https://juejin.im/post/5e58c618e51d4526ed66b5cf

    来源:掘金

    A:

    // 最优解法可以往瀑布流靠拢,有一个三个任务race的池,当一个完成时加入一个未开始的开始。

大厂面试题

本节内容来自网络