2021前端社招总结之编程篇

408 阅读4分钟

这是前端社招面试总结的第二篇编程篇,没看过第一篇算法篇的可以看一下。

前言

前端社招面试,考工程编程题的比例要比纯算法要高。因为编程题中很多问题是实际工作中经常会遇到的,可以实际考察对javascript知识的理解和掌握程度。

面试过程中遇到的编程题

科里化

  1. 自定义加法,a, b 两数相加,a、b 有可能是浮点数
function add(a, b) {
  let count = 1;
  // 当a和b都不是整数时,继续乘10
  while(a !== Math.floor(a) && b !== Math.floor(b)) {
    a *= 10;
    b *= 10;
    count *= 10;
  }

  return (a + b) / count;
}

小tip:面试官可能会问的问题,0.1+0.2===0.3吗,为什么

  1. 优化上面的函数,实现add(0.1)(0.2)(0.3)() === 0.6
function superAdd(a, b) {
  let count = 1;
  // 当a和b都不是整数时,继续乘10
  while(a !== Math.floor(a) && b !== Math.floor(b)) {
    a *= 10;
    b *= 10;
    count *= 10;
  }

  return (a + b) / count;
}

function add(param1) {
  let res = param1 || 0;

  return function innerAdd(param2) {
    res = superAdd(res, param2 || 0);
    // 无参数,返回res
    if(param2 === undefined) {
      return res;
    }
    // 有参数,返回函数
    return innerAdd;
  }
}
  1. 继续优化以上函数,实现add(0.1)(0.2)(0.3) === 0.6
function superAdd(a, b) {
  let count = 1;
  // 当a和b都不是整数时,继续乘10
  while(a !== Math.floor(a) && b !== Math.floor(b)) {
    a *= 10;
    b *= 10;
    count *= 10;
  }

  return (a + b) / count;
}

function add(param1) {
  let res = param1 || 0;

  const innerAdd = function(param2) {
    res = superAdd(res, param2 || 0);
    // 无参数,返回res
    // 有参数,返回函数
    return innerAdd;
  }

  innerAdd.toString = function() {
    return res;
  }

  return innerAdd;
}
  1. 继续优化以上函数,实现不限定参数个数,比如add(0.1, 0.2, 0.3)(0.4) = 1
function superAdd(a, b) {
  let count = 1;
  // 当a和b都不是整数时,继续乘10
  while(a !== Math.floor(a) && b !== Math.floor(b)) {
    a *= 10;
    b *= 10;
    count *= 10;
  }

  return (a + b) / count;
}

function add() {
  let res = 0;
  if(arguments.length) {
    res = [...arguments].reduce((pre, cur) => superAdd(pre, cur));
  }

  const innerAdd = function() {
    if(arguments.length) {
      res = [...arguments].reduce((pre, cur) => superAdd(pre, cur), res);
    }
    // 无参数,返回res
    // 有参数,返回函数
    return innerAdd;
  }

  innerAdd.toString = function() {
    return res;
  }

  return innerAdd;
}

实现LazyMan

实现一个LazyMan,可以按照以下方式调用:
LazyMan(“Hank”)输出:
Hi! This is Hank!

LazyMan(“Hank”).sleep(10).eat(“dinner”)输出
Hi! This is Hank!
//等待10秒..
Wake up after 10
Eat dinner~

LazyMan(“Hank”).eat(“dinner”).eat(“supper”)输出
Hi This is Hank!
Eat dinner~
Eat supper~

LazyMan(“Hank”).sleepFirst(5).eat(“supper”)输出
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper
以此类推。
  1. function的方式
function LazyMan(name) {
  var task = () => { console.log(name); next() };
  var tasks = [task];
  var next = () => {
    if (tasks.length) {
      var tt = tasks.shift();
      tt && tt();
    }
  }
  var res = {
    eat: function (type) {
      tasks.push(() => {
        console.log(type);
        next();
      });
      return res;
    },
    sleep: function (time) {
      var task = () => {
        setTimeout(() => {
          console.log(`sleep ${time}`);
          next();
        }, time)
      }
      tasks.push(task);
      return res;
    },
    sleepFirst: function (time) {
      var task = () => {
        setTimeout(() => {
          console.log(`sleep ${time}`);
          next();
        }, time)
      }
      tasks.unshift(task);
      return res;
    }
  }
  setTimeout(() => { next() });
  return res;
}
LazyMan('Tony').eat('lunch').sleep(10).eat('dinner').sleepFirst(5000);
}
  1. class的方式
class LazyManClass {
  // 构造函数
  constructor(name) {
    this.name = name
    // 定义一个数组存放执行队列
    this.queue = [()=>{
      console.log(`Hi I am ${name}`)
      this.next()
    }]
    setTimeout(() => {
      this.next()
    }, 0)
  }
  //  定义原型方法
  eat(food) {
    var fn = () => {
      console.log(`I am eating ${food}`)
      this.next()
    }
    this.queue.push(fn)
    return this
  }
  sleep(time) {
    var fn = () => {
      // 等待了10秒...
      setTimeout(() => {
        console.log(`等待了${time}秒`)
        this.next()
      }, 1000 * time)
    }
    this.queue.push(fn)
    return this
  }
  sleepFirst(time) {
    var fn = () => {
      // 等待了5秒...
      setTimeout(() => {
        console.log(`等待了${time}秒`)
        this.next()
      }, 1000 * time)
    }
    this.queue.unshift(fn)
    return this
  }
  next() {
    var fn = this.queue.shift()
    fn && fn()
  }
}
function LazyMan(name) {
  return new LazyManClass(name)
}
LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food')

手写系列

  1. 实现一个 EventEmitter 类,作用是进行事件的监听和触发
class EventEmitter {
  /**
   * 实现一个 EventEmitter 类,作用是进行事件的监听和触发
   * @example
   * const ee = new EventEmitter();
   * ee.on('foo', () => {
   *   console.log('Foo event');
   * });
   * ee.on('bar', data => {
   *   console.log(`Bar event with ${data.value}!`)
   * });
   * ee.emit('foo'); // 打印 Foo event 和 Foo event again
   * ee.emit('bar', { value: 1 }); // 打印 Bar Event with 1
   * ee.on('foo', () => {
   *   console.log('Foo event again');
   * });
   */
}
  1. 实现一个 iterate 方法,作用是按照字典序遍历对象
- @param {object} obj 传入的对象
- @returns {object}
- @example
- const obj = {
- c: 1,
- b: 2,
- a: 3
- }
- const iter = iterate(obj);
- console.log(iter.next()); // 打印 a
- console.log(iter.next()); // 打印 b
- console.log(iter.next()); // 打印 c
- console.log(iter.next()); // 打印 undefined
  1. 实现深拷贝
  2. 手写继承
  3. 手写Promise、Promise.all、Promise.race、Promise.finally
  4. 实现Array.isArray
  5. 实现Array.reduce()
  6. 模拟new操作
  7. 其他手写操作可参考(juejin.cn/post/687515…)

网络类

  1. 实现ajax函数
// promise版
function myAjax(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    // 知识点:xhr.open(method, url, async, username, password);第三个参数请求方式是否为异步,默认为true
    xhr.open('GET', url)
    xhr.onreadystatechange = function () {
      // 知识点:readyState 0尚未调用xhr.open 1已调用xhr.open 2已调用xhr.send 3下载中 4下载已完成
      if (this.readyState === 4) {
        if (this.status === 200 || this.status === 304) {
          resolve(this.response)
        } else {
          reject(new Error(this.statusText))
        }
      }
    }
    // 知识点:send(body),body-post请求的数据体
    xhr.send()
  })
}
  1. 实现带超时功能的ajax,超过time时间请求未完成时,取消请求
function myAjax(url, timeout) {
  const ajaxPromise = new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.onreadystatechange = function () {
      if (this.readyState === 4) {
        if (this.status === 200 || this.status === 304) {
          resolve(this.response)
        } else {
          reject(new Error(this.statusText))
        }
      }
    }
    xhr.send()
  })

  return Promise.race(ajaxPromise, new Promise((resolve) => {
    setTimeout(() => {
      resolve('请求超时')
    }, time)
  }))
}
  1. 实现一个异步请求管理方法。有 200 个异步请求要发送出去,但是同时最多只能发送 5 个请求。如何能够最快请求完全部请求。
  2. 手写jsonp

dom类

  1. 获取页面中出现次数最多的节点